From 85f5f295966f3eb182833ea7e79dbf3a8fec3244 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 1 Jan 2016 19:36:12 +0100 Subject: [PATCH 001/110] remove X dependency --- SyntaxKit.xcodeproj/project.pbxproj | 18 +++----- SyntaxKit/Color.swift | 65 +++++++++++++++++++++++++++++ SyntaxKit/Theme.swift | 1 - 3 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 SyntaxKit/Color.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 4a4e5db..20ca5db 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -61,9 +61,8 @@ 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989941B2EC38B00F0D786 /* ResultSet.swift */; }; 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 21F28DDB1B7AC7F8009DD1E9 /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDA1B7AC7F8009DD1E9 /* X.framework */; }; - 21F28DDD1B7AC802009DD1E9 /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDC1B7AC802009DD1E9 /* X.framework */; }; 21F28DDF1B7AC80B009DD1E9 /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDE1B7AC80B009DD1E9 /* X.framework */; }; + 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,6 +137,7 @@ 21F28DDA1B7AC7F8009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/iOS/X.framework; sourceTree = ""; }; 21F28DDC1B7AC802009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/Mac/X.framework; sourceTree = ""; }; 21F28DDE1B7AC80B009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/watchOS/X.framework; sourceTree = ""; }; + 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -145,7 +145,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 21F28DDB1B7AC7F8009DD1E9 /* X.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -162,7 +161,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 21F28DDD1B7AC802009DD1E9 /* X.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,6 +259,7 @@ 211989931B2EC38B00F0D786 /* Result.swift */, 211989941B2EC38B00F0D786 /* ResultSet.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, + 8C08C3C61C36FD6D00D8548F /* Color.swift */, 210299C11B2E8924009C61EE /* Resources */, 210299CD1B2E8924009C61EE /* Tests */, ); @@ -525,6 +524,7 @@ 211989C01B2EC40500F0D786 /* Capture.swift in Sources */, 211989C31B2EC40500F0D786 /* Language.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, + 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, @@ -614,10 +614,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -638,10 +635,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift new file mode 100644 index 0000000..37ada15 --- /dev/null +++ b/SyntaxKit/Color.swift @@ -0,0 +1,65 @@ +// +// Color.swift +// X +// +// Created by Sam Soffes on 4/28/15. +// Copyright (c) 2015 Sam Soffes. All rights reserved. +// + +#if os(OSX) + import AppKit.NSColor + public typealias ColorType = NSColor + + extension NSColor { + public convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { + self.init(SRGBRed: red, green: green, blue: blue, alpha: alpha) + } + } +#else + import UIKit.UIColor + public typealias ColorType = UIColor +#endif + +public typealias Color = ColorType + +extension Color { + public convenience init?(hex s: String) { + var hex: NSString = s + + // Remove `#` and `0x` + if hex.hasPrefix("#") { + hex = hex.substringFromIndex(1) + } else if hex.hasPrefix("0x") { + hex = hex.substringFromIndex(2) + } + + // Invalid if not 3, 6, or 8 characters + let length = hex.length + if length != 3 && length != 6 && length != 8 { + return nil + } + + // Make the string 8 characters long for easier parsing + if length == 3 { + let r = hex.substringWithRange(NSMakeRange(0, 1)) + let g = hex.substringWithRange(NSMakeRange(1, 1)) + let b = hex.substringWithRange(NSMakeRange(2, 1)) + hex = r + r + g + g + b + b + "ff" + } else if length == 6 { + hex = String(hex) + "ff" + } + + // Convert 2 character strings to CGFloats + func hexValue(string: String) -> CGFloat { + let value = Double(strtoul(string, nil, 16)) + return CGFloat(value / 255.0) + } + + let red = hexValue(hex.substringWithRange(NSMakeRange(0, 2))) + let green = hexValue(hex.substringWithRange(NSMakeRange(2, 2))) + let blue = hexValue(hex.substringWithRange(NSMakeRange(4, 2))) + let alpha = hexValue(hex.substringWithRange(NSMakeRange(6, 2))) + + self.init(red: red, green: green, blue: blue, alpha: alpha) + } +} \ No newline at end of file diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 0e22ee2..b105522 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -7,7 +7,6 @@ // import Foundation -import X #if os(iOS) || os(watchOS) import UIKit From 0a7c57b37a674a407dc3780ec0de2ce96e1baff1 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 3 Jan 2016 12:01:22 +0100 Subject: [PATCH 002/110] fix tests not to include X any more --- SyntaxKit.xcodeproj/project.pbxproj | 58 +---------------------------- SyntaxKit/Tests/ParserTests.swift | 2 +- SyntaxKit/Tests/TestHelper.swift | 1 - SyntaxKit/Tests/ThemeTests.swift | 1 - 4 files changed, 3 insertions(+), 59 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 20ca5db..c481deb 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -47,10 +47,6 @@ 211989CE1B2EC40C00F0D786 /* LanguageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210299CF1B2E8924009C61EE /* LanguageTests.swift */; }; 211989CF1B2EC40C00F0D786 /* ThemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898A1B2EBA2C00F0D786 /* ThemeTests.swift */; }; 2122A6EA1B22B9320006409B /* SyntaxKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2122A6DE1B22B9320006409B /* SyntaxKit.framework */; }; - 2132B6201B7BFFA700C2F25B /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDC1B7AC802009DD1E9 /* X.framework */; }; - 2132B6211B7BFFAD00C2F25B /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDA1B7AC7F8009DD1E9 /* X.framework */; }; - 2132B6231B7BFFFD00C2F25B /* X.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDC1B7AC802009DD1E9 /* X.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 2132B6251B7C000A00C2F25B /* X.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDA1B7AC7F8009DD1E9 /* X.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 2198CED21B36D5DE00BD463F /* AttributedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */; }; 2198CED31B36D5DE00BD463F /* Capture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898D1B2EC38B00F0D786 /* Capture.swift */; }; 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */; }; @@ -61,7 +57,6 @@ 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989941B2EC38B00F0D786 /* ResultSet.swift */; }; 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 21F28DDF1B7AC80B009DD1E9 /* X.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F28DDE1B7AC80B009DD1E9 /* X.framework */; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; /* End PBXBuildFile section */ @@ -89,7 +84,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 2132B6231B7BFFFD00C2F25B /* X.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -100,7 +94,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 2132B6251B7C000A00C2F25B /* X.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -134,9 +127,6 @@ 2122A6DE1B22B9320006409B /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 21F28DDA1B7AC7F8009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/iOS/X.framework; sourceTree = ""; }; - 21F28DDC1B7AC802009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/Mac/X.framework; sourceTree = ""; }; - 21F28DDE1B7AC80B009DD1E9 /* X.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = X.framework; path = Carthage/Build/watchOS/X.framework; sourceTree = ""; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -153,7 +143,6 @@ buildActionMask = 2147483647; files = ( 211989B11B2EC3B600F0D786 /* SyntaxKit.framework in Frameworks */, - 2132B6211B7BFFAD00C2F25B /* X.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -169,7 +158,6 @@ buildActionMask = 2147483647; files = ( 2122A6EA1B22B9320006409B /* SyntaxKit.framework in Frameworks */, - 2132B6201B7BFFA700C2F25B /* X.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -177,7 +165,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 21F28DDF1B7AC80B009DD1E9 /* X.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -229,7 +216,6 @@ isa = PBXGroup; children = ( 2122A6E01B22B9320006409B /* SyntaxKit */, - 21F28DD61B7AC7D0009DD1E9 /* Frameworks */, 2122A6DF1B22B9320006409B /* Products */, ); sourceTree = ""; @@ -266,40 +252,6 @@ path = SyntaxKit; sourceTree = ""; }; - 21F28DD61B7AC7D0009DD1E9 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 21F28DD71B7AC7D6009DD1E9 /* OS X */, - 21F28DD81B7AC7DC009DD1E9 /* iOS */, - 21F28DD91B7AC7DF009DD1E9 /* watchOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - 21F28DD71B7AC7D6009DD1E9 /* OS X */ = { - isa = PBXGroup; - children = ( - 21F28DDC1B7AC802009DD1E9 /* X.framework */, - ); - name = "OS X"; - sourceTree = ""; - }; - 21F28DD81B7AC7DC009DD1E9 /* iOS */ = { - isa = PBXGroup; - children = ( - 21F28DDA1B7AC7F8009DD1E9 /* X.framework */, - ); - name = iOS; - sourceTree = ""; - }; - 21F28DD91B7AC7DF009DD1E9 /* watchOS */ = { - isa = PBXGroup; - children = ( - 21F28DDE1B7AC80B009DD1E9 /* X.framework */, - ); - name = watchOS; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -654,10 +606,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = SyntaxKit/Tests/Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -672,10 +621,7 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = SyntaxKit/Tests/Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 8d0e4a4..643233f 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -7,7 +7,7 @@ // import XCTest -import SyntaxKit +@testable import SyntaxKit class ParserTests: XCTestCase { diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 2772dbf..a1f5dde 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -8,7 +8,6 @@ import Foundation import XCTest -import X @testable import SyntaxKit func fixture(name: String, _ type: String) -> String! { diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index fa8471d..1c00a65 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -7,7 +7,6 @@ // import XCTest -import X @testable import SyntaxKit class ThemeTests: XCTestCase { From 2552f1c5368ec209e6e2111fc8468972152b858b Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 3 Jan 2016 12:16:45 +0100 Subject: [PATCH 003/110] revert erroneous import --- SyntaxKit/Tests/ParserTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 643233f..8d0e4a4 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -7,7 +7,7 @@ // import XCTest -@testable import SyntaxKit +import SyntaxKit class ParserTests: XCTestCase { From 4515c1ed65ffece606a354ac3e2b927bfb81fb76 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 5 Jan 2016 01:31:20 +0100 Subject: [PATCH 004/110] small consistency changes --- Cartfile | 2 +- Cartfile.resolved | 2 +- SyntaxKit.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index c5e7485..05140e2 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "soffes/X" "swift2" +github "swift2" diff --git a/Cartfile.resolved b/Cartfile.resolved index d0ce15e..27c1d7f 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "soffes/X" "48d1541834487a4ecb0bb4b37ac1621dfd84ec8f" +github "48d1541834487a4ecb0bb4b37ac1621dfd84ec8f" diff --git a/SyntaxKit.podspec b/SyntaxKit.podspec index 57d6ff7..1d827f5 100644 --- a/SyntaxKit.podspec +++ b/SyntaxKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'SyntaxKit' - spec.version = '0.1.0' + spec.version = '0.1.1' spec.authors = {'Sam Soffes' => 'sam@soff.es'} spec.homepage = 'https://github.com/soffes/SyntaxKit' spec.summary = 'TextMate-style syntax highlighting.' From 8de5f907e1ac031090463bac03cb3db1aa3d76ef Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 9 Jan 2016 17:10:29 +0100 Subject: [PATCH 005/110] Get SyntaxKit working Fix range bugs. Add Classes to handle entries in the tmbundles correctly. Extend tests. --- SyntaxKit.xcodeproj/project.pbxproj | 24 ++++++++++ SyntaxKit/Include.swift | 15 ++++++ SyntaxKit/Language.swift | 40 ++++++---------- SyntaxKit/Parser.swift | 62 +++++++++++++------------ SyntaxKit/Pattern.swift | 72 +++++++++++++++++++---------- SyntaxKit/Patterns.swift | 39 ++++++++++++++++ SyntaxKit/Repository.swift | 35 ++++++++++++++ SyntaxKit/Tests/LanguageTests.swift | 15 ++++-- SyntaxKit/Tests/ParserTests.swift | 4 ++ 9 files changed, 220 insertions(+), 86 deletions(-) create mode 100644 SyntaxKit/Include.swift create mode 100644 SyntaxKit/Patterns.swift create mode 100644 SyntaxKit/Repository.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index c481deb..83a5d74 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -58,6 +58,15 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8CEEC0DE1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; + 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; + 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; + 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; + 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; + 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; + 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; + 8CEEC0E71C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; + 8CEEC0E81C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -128,6 +137,9 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patterns.swift; sourceTree = ""; }; + 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; + 8CEEC0E51C4120A900BF3E85 /* Include.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Include.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -242,6 +254,9 @@ 211989901B2EC38B00F0D786 /* Language.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, + 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */, + 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, + 8CEEC0E51C4120A900BF3E85 /* Include.swift */, 211989931B2EC38B00F0D786 /* Result.swift */, 211989941B2EC38B00F0D786 /* ResultSet.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, @@ -478,11 +493,14 @@ 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, + 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, + 8CEEC0E71C4120A900BF3E85 /* Include.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, 211989C81B2EC40500F0D786 /* Theme.swift in Sources */, 211989C61B2EC40500F0D786 /* Result.swift in Sources */, 211989BF1B2EC40500F0D786 /* AttributedParser.swift in Sources */, + 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -506,8 +524,11 @@ 2119899B1B2EC38B00F0D786 /* Language.swift in Sources */, 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */, 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */, + 8CEEC0DE1C411C8600BF3E85 /* Patterns.swift in Sources */, + 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, + 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, 211989971B2EC38B00F0D786 /* AttributedParser.swift in Sources */, @@ -534,8 +555,11 @@ 2198CED61B36D5DE00BD463F /* Language.swift in Sources */, 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, + 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */, + 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, + 8CEEC0E81C4120A900BF3E85 /* Include.swift in Sources */, 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */, 2198CED91B36D5DE00BD463F /* Result.swift in Sources */, 2198CED21B36D5DE00BD463F /* AttributedParser.swift in Sources */, diff --git a/SyntaxKit/Include.swift b/SyntaxKit/Include.swift new file mode 100644 index 0000000..3958eec --- /dev/null +++ b/SyntaxKit/Include.swift @@ -0,0 +1,15 @@ +// +// Include.swift +// SyntaxKit +// +// TODO: add support for more sophisticiated includes +// +// Created by Alexander Hedges on 09/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import Foundation + +class Include { + +} \ No newline at end of file diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index cbb5dba..a1a935d 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -15,7 +15,7 @@ public struct Language { public let UUID: String public let name: String public let scopeName: String - let patterns: [Pattern] + let patterns: Patterns // MARK: - Initializers @@ -30,31 +30,17 @@ public struct Language { self.name = name self.scopeName = scopeName - var repository = [String: Pattern]() - if let repo = dictionary["repository"] as? [String: [NSObject: AnyObject]] { - for (key, value) in repo { - if let pattern = Pattern(dictionary: value) { - repository[key] = pattern - } - } - } - - var patterns = [Pattern]() - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - for value in array { - if let include = value["include"] as? String { - let key = include.substringFromIndex(include.startIndex.successor()) - if let pattern = repository[key] { - patterns.append(pattern) - continue - } - } - - if let pattern = Pattern(dictionary: value) { - patterns.append(pattern) - } - } - } - self.patterns = patterns + let repository: Repository + if let repo = dictionary["repository"] as? [String: [NSObject: AnyObject]] { + repository = Repository(repo: repo) + } else { + repository = Repository(repo: [:]) + } + + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + self.patterns = Patterns(array: array, repository: repository) + } else { + self.patterns = Patterns(array: [], repository: repository) + } } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 9b8fed0..e5178c2 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -46,9 +46,10 @@ public class Parser { // Loop through the line until we reach the end while range.length > 0 && range.location < limit { - let location = parse(string, inRange: range, callback: callback) + let location = parse(string, inRange: range, withPatterns: language.patterns, callback: callback) range.location = Int(location) - range.length = max(0, range.length - paragraphRange.location - range.location) +// assert(range.length - (range.location - paragraphRange.location) >= 0) + range.length = max(0, range.length - (range.location - paragraphRange.location)) } } } @@ -57,24 +58,27 @@ public class Parser { // MARK: - Private /// Returns new location - private func parse(string: String, inRange bounds: NSRange, callback: Callback) -> UInt { - for pattern in language.patterns { - // Single pattern + private func parse(string: String, inRange bounds: NSRange, withPatterns patterns: Patterns, callback: Callback) -> UInt { + for pattern in patterns.getContent() { +// print(pattern.name) if let match = pattern.match { - if let resultSet = parse(string, inRange: bounds, scope: pattern.name, expression: match, captures: pattern.captures) { + if let resultSet = parse(string, inRange: bounds, expression: match, captures: pattern.captures, scope: pattern.name) { return applyResults(resultSet, callback: callback) - } else { - continue } - } - - // Begin & end - if let begin = pattern.begin, end = pattern.end { + } else if let begin = pattern.begin, end = pattern.end { guard let beginResults = parse(string, inRange: bounds, expression: begin, captures: pattern.beginCaptures), beginRange = beginResults.range else { continue } - - let location = NSMaxRange(beginRange) - let endBounds = NSMakeRange(location, bounds.length - location - bounds.location) + + var location = NSMaxRange(beginRange) +// assert(bounds.length - (location - bounds.location) >= 0) +// let midBounds = NSRange(location: location, length: bounds.length - (location - bounds.location)) +// +// if pattern.subpatterns.getContent().count >= 1 { +// location = Int(parse(string, inRange: midBounds, withPatterns: pattern.subpatterns, callback: callback)) +// } + + assert(bounds.length - (location - bounds.location) >= 0) + let endBounds = NSRange(location: location, length: bounds.length - (location - bounds.location)) // might still need max(0, …) for extra security guard let endResults = parse(string, inRange: endBounds, expression: end, captures: pattern.endCaptures), endRange = endResults.range else { /* TODO: Rewind? */ continue } @@ -89,22 +93,24 @@ public class Parser { results.addResults(endResults) return applyResults(results, callback: callback) - } + } else if pattern.subpatterns.getContent().count >= 1 { + let subPatternTry = parse(string, inRange: bounds, withPatterns: pattern.subpatterns, callback: callback) + if subPatternTry < UInt(NSMaxRange(bounds)) { + return subPatternTry + } + } } return UInt(NSMaxRange(bounds)) } /// Parse an expression with captures - private func parse(string: String, inRange bounds: NSRange, scope: String? = nil, expression expressionString: String, captures: CaptureCollection?) -> ResultSet? { - let matches: [NSTextCheckingResult] - do { - let expression = try NSRegularExpression(pattern: expressionString, options: [.CaseInsensitive]) - matches = expression.matchesInString(string, options: [], range: bounds) - } catch { - return nil - } - + private func parse(string: String, inRange bounds: NSRange, expression regularExpression: NSRegularExpression?, captures: CaptureCollection?, scope: String? = nil) -> ResultSet? { + if regularExpression == nil { + return nil + } + let matches = regularExpression!.matchesInString(string, options: [], range: bounds) + guard let result = matches.first else { return nil } var resultSet = ResultSet() @@ -125,11 +131,7 @@ public class Parser { } } - if !resultSet.isEmpty { - return resultSet - } - - return nil + return resultSet } private func applyResults(resultSet: ResultSet, callback: Callback) -> UInt { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index fa2453f..54f935e 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -13,31 +13,47 @@ final class Pattern { // MARK: - Properties let name: String? - let match: String? + let match: NSRegularExpression? let captures: CaptureCollection? - let begin: String? + let begin: NSRegularExpression? let beginCaptures: CaptureCollection? - let end: String? + let end: NSRegularExpression? let endCaptures: CaptureCollection? private weak var parent: Pattern? - private let patterns: [Pattern] + private var patterns: Patterns var superpattern: Pattern? { return parent } - var subpatterns: [Pattern] { + var subpatterns: Patterns { return patterns } // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject], parent: Pattern? = nil) { + init?(dictionary: [NSObject: AnyObject], repository: Repository, parent: Pattern? = nil) { self.parent = parent self.name = dictionary["name"] as? String - self.match = dictionary["match"] as? String - self.begin = dictionary["begin"] as? String - self.end = dictionary["end"] as? String + + if let matchExpr = dictionary["match"] as? String { + self.match = try? NSRegularExpression(pattern: matchExpr, options:.AnchorsMatchLines) //[.CaseInsensitive] + } else { + self.match = nil + } + + if let beginExpr = dictionary["begin"] as? String { + self.begin = try? NSRegularExpression(pattern: beginExpr, options:.AnchorsMatchLines) + } else { + self.begin = nil + } + + if let endExpr = dictionary["end"] as? String { + self.end = try? NSRegularExpression(pattern: endExpr, options:.AnchorsMatchLines) + } else { + self.end = nil + } + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { self.beginCaptures = CaptureCollection(dictionary: dictionary) @@ -52,19 +68,27 @@ final class Pattern { } if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { - self.endCaptures = CaptureCollection(dictionary: dictionary) - } else { - self.endCaptures = nil - } - - var patterns = [Pattern]() - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - for value in array { - if let pattern = Pattern(dictionary: value, parent: parent) { - patterns.append(pattern) - } - } - } - self.patterns = patterns - } + self.endCaptures = CaptureCollection(dictionary: dictionary) + } else { + self.endCaptures = nil + } + + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + self.patterns = Patterns(array: array, repository: repository) + } else { + self.patterns = Patterns(array: [], repository: repository) + } + } + + init(pattern: Pattern, parent: Pattern? = nil) { + self.name = pattern.name + self.match = pattern.match + self.captures = pattern.captures + self.begin = pattern.begin + self.beginCaptures = pattern.beginCaptures + self.end = pattern.end + self.endCaptures = pattern.endCaptures + self.parent = parent + self.patterns = pattern.patterns + } } diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift new file mode 100644 index 0000000..a9b49d7 --- /dev/null +++ b/SyntaxKit/Patterns.swift @@ -0,0 +1,39 @@ +// +// Patterns.swift +// SyntaxKit +// +// Created by Alexander Hedges on 09/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import Foundation + +class Patterns { + private var content: [Pattern] + + init(array: [[NSObject: AnyObject]], repository: Repository) { + + content = [] + for value in array { + if let include = value["include"] as? String { + if include.hasPrefix("#") { + let key = include.substringFromIndex(include.startIndex.successor()) + if let pattern = repository[key] { + content.append(pattern) + } //else if key == self.name { +// let newSelf = Pattern(pattern: self, parent: nil) +// self.patterns.append(newSelf) +// } + } else if include == "$self" { + // TODO: recursive stuff + } + } else if let pattern = Pattern(dictionary: value, repository: repository) { + content.append(pattern) + } + } + } + + func getContent() -> [Pattern] { + return content + } +} \ No newline at end of file diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift new file mode 100644 index 0000000..99cab56 --- /dev/null +++ b/SyntaxKit/Repository.swift @@ -0,0 +1,35 @@ +// +// Repository.swift +// SyntaxKit +// +// Created by Alexander Hedges on 09/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import Foundation + +class Repository { + private var entries: [String: Pattern] + + init(repo: [String: [NSObject: AnyObject]]) { + self.entries = [:] + for (key, value) in repo { + if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { + let newEntries = NSMutableDictionary(dictionary: self.entries) + newEntries.addEntriesFromDictionary(Repository(repo: containedRepo).allEntries()) + self.entries = newEntries.copy() as! [String: Pattern] + } + if let pattern = Pattern(dictionary: value, repository: self) { + self.entries[key] = pattern + } + } + } + + func allEntries() -> [String: Pattern] { + return entries + } + + subscript(index: String) -> Pattern? { + return entries[index] + } +} \ No newline at end of file diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 4eb0662..0cdce46 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -23,11 +23,16 @@ class LanguageTests: XCTestCase { XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) - XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns[0].name!) - XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns[1].name!) - XCTAssertEqual("constant.numeric.yaml", yaml.patterns[2].name!) - - let pattern = yaml.patterns[3] + XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns.getContent()[0].name!) + XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns.getContent()[0].beginCaptures![0]!.name) + XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns.getContent()[0].endCaptures![0]!.name) + XCTAssertEqual("punctuation.definition.comment.ruby", yaml.patterns.getContent()[0].subpatterns.getContent()[0].captures![1]!.name) + XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns.getContent()[1].name!) + XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns.getContent()[1].beginCaptures![2]!.name) + XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns.getContent()[1].beginCaptures![5]!.name) + XCTAssertEqual("constant.numeric.yaml", yaml.patterns.getContent()[2].name!) + + let pattern = yaml.patterns.getContent()[3] XCTAssertEqual("string.unquoted.yaml", pattern.name!) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures![1]!.name) } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 8d0e4a4..4456b8c 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -53,6 +53,10 @@ class ParserTests: XCTestCase { XCTAssertEqual(NSMakeRange(39, 4), stringQuoted!) } + + func testParsingGarbage() { + parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } + } func testRuby() { let parser = Parser(language: language("Ruby")) From 0b66d4517534eb20d8620bef08e5882ee9c1b395 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Wed, 13 Jan 2016 17:59:24 +0100 Subject: [PATCH 006/110] improve parser (Try II) --- SyntaxKit/Parser.swift | 172 ++++++++++++++++++++++++++-------------- SyntaxKit/Pattern.swift | 6 +- 2 files changed, 116 insertions(+), 62 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index e5178c2..3077982 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -36,87 +36,139 @@ public class Parser { var paragraphEnd = 0 while paragraphEnd < length { - var paragraphStart = 0 - var contentsEnd = 0 - s.getParagraphStart(¶graphStart, end: ¶graphEnd, contentsEnd: &contentsEnd, forRange: NSMakeRange(paragraphEnd, 0)) + let paragraphStart = paragraphEnd + var newParagraphStart = 0 + s.getParagraphStart(&newParagraphStart, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) + if newParagraphStart < paragraphStart - 1 { + newParagraphStart = paragraphStart + } - let paragraphRange = NSMakeRange(paragraphStart, contentsEnd - paragraphStart) + let paragraphRange = NSMakeRange(newParagraphStart, paragraphEnd - newParagraphStart) let limit = NSMaxRange(paragraphRange) var range = paragraphRange // Loop through the line until we reach the end while range.length > 0 && range.location < limit { - let location = parse(string, inRange: range, withPatterns: language.patterns, callback: callback) - range.location = Int(location) -// assert(range.length - (range.location - paragraphRange.location) >= 0) - range.length = max(0, range.length - (range.location - paragraphRange.location)) + print(range) + print(s.substringWithRange(range)) + if s.substringWithRange(range).containsString("|") { + print("There") + } + let matches = matchPatterns(language.patterns, withString: string, inRange: range) + var newLocation = limit + if matches != nil && matches!.results.count != 0 { + let newPlace = Int(applyResults(matches!, callback: callback)) + if newPlace != range.location { + newLocation = newPlace + } + } + if newLocation > limit { + paragraphEnd = newLocation + break + } + assert(range.length - (newLocation - range.location) >= 0) + range.length = range.length - (newLocation - range.location) + range.location = newLocation } } } // MARK: - Private - - /// Returns new location - private func parse(string: String, inRange bounds: NSRange, withPatterns patterns: Patterns, callback: Callback) -> UInt { - for pattern in patterns.getContent() { -// print(pattern.name) - if let match = pattern.match { - if let resultSet = parse(string, inRange: bounds, expression: match, captures: pattern.captures, scope: pattern.name) { - return applyResults(resultSet, callback: callback) - } - } else if let begin = pattern.begin, end = pattern.end { - guard let beginResults = parse(string, inRange: bounds, expression: begin, captures: pattern.beginCaptures), - beginRange = beginResults.range else { continue } + + private func matchPatterns(patterns: Patterns, withString string: String, inRange bounds: NSRange) -> ResultSet? { + for pattern in patterns.getContent() { + if let match = pattern.match { + if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + if !resultSet.isEmpty && resultSet.range!.length != 0 && resultSet.range!.location <= firstNonWhitespaceLocationInString(string, withRange: bounds) { + return resultSet + } + } + } else if let begin = pattern.begin, end = pattern.end { + let s: NSString = string + let startBounds = NSRange(location: bounds.location, length: s.length - bounds.location) + guard let beginResults = matchExpression(begin, withString: string, inRange: startBounds , captures: pattern.beginCaptures), + beginRange = beginResults.range else { continue } - var location = NSMaxRange(beginRange) -// assert(bounds.length - (location - bounds.location) >= 0) -// let midBounds = NSRange(location: location, length: bounds.length - (location - bounds.location)) -// -// if pattern.subpatterns.getContent().count >= 1 { -// location = Int(parse(string, inRange: midBounds, withPatterns: pattern.subpatterns, callback: callback)) -// } + if beginRange.location > firstNonWhitespaceLocationInString(string, withRange: bounds) { + continue + } - assert(bounds.length - (location - bounds.location) >= 0) - let endBounds = NSRange(location: location, length: bounds.length - (location - bounds.location)) // might still need max(0, …) for extra security - - guard let endResults = parse(string, inRange: endBounds, expression: end, captures: pattern.endCaptures), - endRange = endResults.range else { /* TODO: Rewind? */ continue } - - // Add whole scope before start and end - var results = ResultSet() - if let name = pattern.name { - results.addResult(Result(scope: name, range: NSUnionRange(beginRange, endRange))) - } - - results.addResults(beginResults) - results.addResults(endResults) - - return applyResults(results, callback: callback) + var newLocation = NSMaxRange(beginRange) + + + assert(s.length - newLocation >= 0) + var endBounds = NSRange(location: newLocation, length: s.length - newLocation) + + var midResults = ResultSet() + + if pattern.subpatterns.getContent().count >= 1 { + let midTestResults = matchPatterns(pattern.subpatterns, withString: string, inRange: endBounds) + let midRange = midTestResults?.range + if midRange != nil && midRange!.location <= firstNonWhitespaceLocationInString(string, withRange: midRange!) { + midResults.addResults(midTestResults!) + newLocation = NSMaxRange(midRange!) + assert(endBounds.length - (newLocation - endBounds.location) >= 0) + endBounds = NSRange(location: newLocation, length: endBounds.length - (newLocation - endBounds.location)) + } + } + + let endResults = matchExpression(end, withString: string, inRange: endBounds, captures: pattern.endCaptures) + let endRange = endResults?.range + if endRange == nil { + continue + } + + var results = ResultSet() + if let name = pattern.name { + results.addResult(Result(scope: name, range: NSUnionRange(beginRange, endRange!))) + } + + results.addResults(beginResults) + results.addResults(midResults) + results.addResults(endResults!) + + return results } else if pattern.subpatterns.getContent().count >= 1 { - let subPatternTry = parse(string, inRange: bounds, withPatterns: pattern.subpatterns, callback: callback) - if subPatternTry < UInt(NSMaxRange(bounds)) { + let subPatternTry = matchPatterns(pattern.subpatterns, withString: string, inRange: bounds) + if subPatternTry != nil { return subPatternTry } } - } - - return UInt(NSMaxRange(bounds)) - } - - /// Parse an expression with captures - private func parse(string: String, inRange bounds: NSRange, expression regularExpression: NSRegularExpression?, captures: CaptureCollection?, scope: String? = nil) -> ResultSet? { + } + return nil + } + + private func firstNonWhitespaceLocationInString(string: String, withRange range: NSRange) -> Int { + for i in range.toRange()! { + if isblank(Int32((string as NSString).characterAtIndex(i))) == 0 { + return i + } + } + return NSMaxRange(range) + } + + /// Matches a given regular expression in a String + /// + /// + /// - returns: The resultset + private func matchExpression(regularExpression: NSRegularExpression?, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { if regularExpression == nil { return nil } - let matches = regularExpression!.matchesInString(string, options: [], range: bounds) + let matches = regularExpression!.matchesInString(string, options: [.WithTransparentBounds], range: bounds) + if matches.first == nil || matches.first!.range.location == NSNotFound { + return nil + } + + let result = matches.first! - guard let result = matches.first else { return nil } - var resultSet = ResultSet() - if let scope = scope where result.range.location != NSNotFound { - resultSet.addResult(Result(scope: scope, range: result.range)) - } + if baseSelector != nil { + resultSet.addResult(Result(scope: baseSelector!, range: result.range)) + } else { + resultSet.addResult(Result(scope: "", range: result.range)) + } if let captures = captures { for index in captures.captureIndexes { @@ -137,7 +189,9 @@ public class Parser { private func applyResults(resultSet: ResultSet, callback: Callback) -> UInt { var i = 0 for result in resultSet.results { - callback(scope: result.scope, range: result.range) + if result.scope != "" && result.range.length > 0 { + callback(scope: result.scope, range: result.range) + } i = max(NSMaxRange(result.range), i) } return UInt(i) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 54f935e..82ba11b 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -37,19 +37,19 @@ final class Pattern { self.name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - self.match = try? NSRegularExpression(pattern: matchExpr, options:.AnchorsMatchLines) //[.CaseInsensitive] + self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] } else { self.match = nil } if let beginExpr = dictionary["begin"] as? String { - self.begin = try? NSRegularExpression(pattern: beginExpr, options:.AnchorsMatchLines) + self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) } else { self.begin = nil } if let endExpr = dictionary["end"] as? String { - self.end = try? NSRegularExpression(pattern: endExpr, options:.AnchorsMatchLines) + self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { self.end = nil } From 96ae90e4de284cc6150ffa7e3012ec16ecdba116 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 14 Jan 2016 12:04:47 +0100 Subject: [PATCH 007/110] Adjust Parser no more prints and more tight matching --- SyntaxKit/Parser.swift | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 3077982..dec988b 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -49,11 +49,6 @@ public class Parser { // Loop through the line until we reach the end while range.length > 0 && range.location < limit { - print(range) - print(s.substringWithRange(range)) - if s.substringWithRange(range).containsString("|") { - print("There") - } let matches = matchPatterns(language.patterns, withString: string, inRange: range) var newLocation = limit if matches != nil && matches!.results.count != 0 { @@ -85,9 +80,7 @@ public class Parser { } } } else if let begin = pattern.begin, end = pattern.end { - let s: NSString = string - let startBounds = NSRange(location: bounds.location, length: s.length - bounds.location) - guard let beginResults = matchExpression(begin, withString: string, inRange: startBounds , captures: pattern.beginCaptures), + guard let beginResults = matchExpression(begin, withString: string, inRange: bounds , captures: pattern.beginCaptures), beginRange = beginResults.range else { continue } if beginRange.location > firstNonWhitespaceLocationInString(string, withRange: bounds) { @@ -96,7 +89,7 @@ public class Parser { var newLocation = NSMaxRange(beginRange) - + let s: NSString = string assert(s.length - newLocation >= 0) var endBounds = NSRange(location: newLocation, length: s.length - newLocation) From b39d05b9f11ea717a3e672adcbff234f9bd13aa4 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 19 Jan 2016 21:44:00 +0100 Subject: [PATCH 008/110] parser try III --- SyntaxKit.xcodeproj/project.pbxproj | 26 +- .../xcschemes/SyntaxKit-iOS.xcscheme | 3 +- SyntaxKit/AttributedParser.swift | 4 +- SyntaxKit/Parser.swift | 75 +- SyntaxKit/ResultSet.swift | 15 +- .../Resources/Fixtures/Solarized.tmTheme | 2148 +++++++++++++++++ .../Tests/Resources/Fixtures/Swift.tmLanguage | 1522 ++++++++++++ .../Resources/Fixtures/swifttest.swift.txt | 75 + .../SwiftBaselineHighlightingTests.swift | 61 + SyntaxKit/Tests/ThemeTests.swift | 10 +- 10 files changed, 3890 insertions(+), 49 deletions(-) create mode 100644 SyntaxKit/Tests/Resources/Fixtures/Solarized.tmTheme create mode 100644 SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage create mode 100644 SyntaxKit/Tests/Resources/Fixtures/swifttest.swift.txt create mode 100644 SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 83a5d74..6ac470f 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -58,6 +58,14 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; + 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; + 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; + 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; + 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; + 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; + 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; + 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; 8CEEC0DE1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; @@ -137,6 +145,10 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; + 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Solarized.tmTheme; sourceTree = ""; }; + 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Swift.tmLanguage; sourceTree = ""; }; + 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patterns.swift; sourceTree = ""; }; 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; 8CEEC0E51C4120A900BF3E85 /* Include.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Include.swift; sourceTree = ""; }; @@ -198,6 +210,7 @@ 211989821B2EB18000F0D786 /* TestHelper.swift */, 211989881B2EB8D400F0D786 /* ParserTests.swift */, 210299D01B2E8924009C61EE /* AttributedParserTests.swift */, + 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */, 210299CF1B2E8924009C61EE /* LanguageTests.swift */, 2119898A1B2EBA2C00F0D786 /* ThemeTests.swift */, ); @@ -217,7 +230,10 @@ isa = PBXGroup; children = ( 210BF26F1B37C0A2008AA4F0 /* Ruby.tmLanguage */, + 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */, + 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */, 210BF26C1B37C04E008AA4F0 /* test.rb.txt */, + 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */, 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */, 2119897F1B2EAF0900F0D786 /* YAML.tmLanguage */, ); @@ -449,10 +465,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 210BF2711B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */, + 211989CA1B2EC40900F0D786 /* YAML.tmLanguage in Resources */, 211989C91B2EC40900F0D786 /* Tomorrow.tmTheme in Resources */, + 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, 210BF26E1B37C04E008AA4F0 /* test.rb.txt in Resources */, - 211989CA1B2EC40900F0D786 /* YAML.tmLanguage in Resources */, + 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -471,6 +490,8 @@ 211989801B2EAF0900F0D786 /* Tomorrow.tmTheme in Resources */, 210BF26D1B37C04E008AA4F0 /* test.rb.txt in Resources */, 211989811B2EAF0900F0D786 /* YAML.tmLanguage in Resources */, + 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, + 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -512,6 +533,7 @@ 211989CC1B2EC40C00F0D786 /* ParserTests.swift in Sources */, 211989CE1B2EC40C00F0D786 /* LanguageTests.swift in Sources */, 211989CB1B2EC40C00F0D786 /* TestHelper.swift in Sources */, + 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, 211989CD1B2EC40C00F0D786 /* AttributedParserTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -542,7 +564,9 @@ 211989841B2EB18600F0D786 /* TestHelper.swift in Sources */, 211989891B2EB8D400F0D786 /* ParserTests.swift in Sources */, 210299DF1B2E892E009C61EE /* AttributedParserTests.swift in Sources */, + 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */, 210299DE1B2E892E009C61EE /* LanguageTests.swift in Sources */, + 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, 2119898B1B2EBA2C00F0D786 /* ThemeTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index e630ca1..188cf4a 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -26,8 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 0785482..9248fd6 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -55,8 +55,8 @@ public class AttributedParser: Parser { } var attributes = Attributes() - for i in 0.. 0 && range.location < limit { +// print(range) +// print(s.substringWithRange(range)) +// if s.substringWithRange(range).containsString("class A < B") { +// print("There") +// } let matches = matchPatterns(language.patterns, withString: string, inRange: range) var newLocation = limit - if matches != nil && matches!.results.count != 0 { - let newPlace = Int(applyResults(matches!, callback: callback)) + if matches.results.count != 0 { + let newPlace = Int(applyResults(matches, callback: callback)) if newPlace != range.location { newLocation = newPlace } @@ -71,22 +76,20 @@ public class Parser { // MARK: - Private - private func matchPatterns(patterns: Patterns, withString string: String, inRange bounds: NSRange) -> ResultSet? { + private func matchPatterns(patterns: Patterns, withString string: String, inRange bounds: NSRange) -> ResultSet { + var bestResult = ResultSet() for pattern in patterns.getContent() { +// print("Pattern: \(pattern.name)") if let match = pattern.match { if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { - if !resultSet.isEmpty && resultSet.range!.length != 0 && resultSet.range!.location <= firstNonWhitespaceLocationInString(string, withRange: bounds) { - return resultSet + if !resultSet.isEmpty && resultSet.range!.length != 0 && bestResult.hasLowerPriorityThan(resultSet) { + bestResult = resultSet } } } else if let begin = pattern.begin, end = pattern.end { - guard let beginResults = matchExpression(begin, withString: string, inRange: bounds , captures: pattern.beginCaptures), - beginRange = beginResults.range else { continue } - - if beginRange.location > firstNonWhitespaceLocationInString(string, withRange: bounds) { - continue - } + guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { continue } + let beginRange = beginResults.range! var newLocation = NSMaxRange(beginRange) let s: NSString = string @@ -97,9 +100,9 @@ public class Parser { if pattern.subpatterns.getContent().count >= 1 { let midTestResults = matchPatterns(pattern.subpatterns, withString: string, inRange: endBounds) - let midRange = midTestResults?.range - if midRange != nil && midRange!.location <= firstNonWhitespaceLocationInString(string, withRange: midRange!) { - midResults.addResults(midTestResults!) + let midRange = midTestResults.range + if midRange != nil { + midResults.addResults(midTestResults) newLocation = NSMaxRange(midRange!) assert(endBounds.length - (newLocation - endBounds.location) >= 0) endBounds = NSRange(location: newLocation, length: endBounds.length - (newLocation - endBounds.location)) @@ -107,49 +110,41 @@ public class Parser { } let endResults = matchExpression(end, withString: string, inRange: endBounds, captures: pattern.endCaptures) - let endRange = endResults?.range - if endRange == nil { + if endResults == nil { continue } + let endRange = endResults!.range! var results = ResultSet() if let name = pattern.name { - results.addResult(Result(scope: name, range: NSUnionRange(beginRange, endRange!))) + results.addResult(Result(scope: name, range: NSUnionRange(beginRange, endRange))) } results.addResults(beginResults) results.addResults(midResults) results.addResults(endResults!) - - return results + if bestResult.hasLowerPriorityThan(results) { + bestResult = results + } } else if pattern.subpatterns.getContent().count >= 1 { - let subPatternTry = matchPatterns(pattern.subpatterns, withString: string, inRange: bounds) - if subPatternTry != nil { - return subPatternTry + var subPatternTry = matchPatterns(pattern.subpatterns, withString: string, inRange: bounds) + if bestResult.hasLowerPriorityThan(subPatternTry) { + if pattern.name != nil && subPatternTry.range != nil { + subPatternTry.addResult(Result(scope: pattern.name!, range: subPatternTry.range!)) + } + bestResult = subPatternTry } } } - return nil + return bestResult } - private func firstNonWhitespaceLocationInString(string: String, withRange range: NSRange) -> Int { - for i in range.toRange()! { - if isblank(Int32((string as NSString).characterAtIndex(i))) == 0 { - return i - } - } - return NSMaxRange(range) - } - /// Matches a given regular expression in a String /// /// /// - returns: The resultset - private func matchExpression(regularExpression: NSRegularExpression?, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { - if regularExpression == nil { - return nil - } - let matches = regularExpression!.matchesInString(string, options: [.WithTransparentBounds], range: bounds) + private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { + let matches = regularExpression.matchesInString(string, options: [], range: bounds) if matches.first == nil || matches.first!.range.location == NSNotFound { return nil } @@ -157,11 +152,7 @@ public class Parser { let result = matches.first! var resultSet = ResultSet() - if baseSelector != nil { - resultSet.addResult(Result(scope: baseSelector!, range: result.range)) - } else { - resultSet.addResult(Result(scope: "", range: result.range)) - } + resultSet.addResult(Result(scope: baseSelector ?? "", range: result.range)) if let captures = captures { for index in captures.captureIndexes { diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 839d6d0..20b8d18 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -22,7 +22,20 @@ struct ResultSet { var isEmpty: Bool { return results.isEmpty } - + + // MARK: - Comparing + + func hasLowerPriorityThan(other: ResultSet?) -> Bool { + if other == nil || other!.range == nil { + return false + } else if self.range == nil { + return true + } else if self.range!.location != other!.range!.location { + return self.range!.location > other!.range!.location + } else { + return self.range!.length < other!.range!.length + } + } // MARK: - Adding diff --git a/SyntaxKit/Tests/Resources/Fixtures/Solarized.tmTheme b/SyntaxKit/Tests/Resources/Fixtures/Solarized.tmTheme new file mode 100644 index 0000000..8e85ba1 --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/Solarized.tmTheme @@ -0,0 +1,2148 @@ + + + + + name + Solarized (light) + semanticClass + solarized.light + gutterSettings + + background + #EEE8D5 + divider + #EEE8D5 + foreground + #93A1A1 + selectionBackground + #EEE8D5 + selectionForeground + #93A1A1 + + settings + + + settings + + background + #FDF6E3 + caret + #000000 + foreground + #586E75 + invisibles + #EAE3C9 + lineHighlight + #EEE8D5 + selection + #EEE8D5 + + + + name + Comment + scope + comment + settings + + fontStyle + + foreground + #93A1A1 + + + + name + String + scope + string + settings + + foreground + #2aa198 + + + + name + StringNumber + scope + string + settings + + foreground + #586E75 + + + + name + Regexp + scope + string.regexp + settings + + foreground + #dc322f + + + + name + Number + scope + constant.numeric + settings + + foreground + #d33682 + + + + name + Variable + scope + variable.language, variable.other + settings + + foreground + #268bd2 + + + + name + Keyword + scope + keyword + settings + + foreground + #859900 + + + + name + Storage + scope + storage + settings + + fontStyle + bold + foreground + #073642 + + + + name + Class name + scope + entity.name.class, entity.name.type.class + settings + + foreground + #268bd2 + + + + name + Function name + scope + entity.name.function + settings + + foreground + #268bd2 + + + + name + Variable start + scope + punctuation.definition.variable + settings + + foreground + #859900 + + + + name + Embedded code markers + scope + punctuation.section.embedded.begin, punctuation.section.embedded.end + settings + + foreground + #dc322f + + + + name + Built-in constant + scope + constant.language, meta.preprocessor + settings + + foreground + #B58900 + + + + name + Support.construct + scope + support.function.construct, keyword.other.new + settings + + foreground + #dc322f + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #CB4B16 + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + + + name + Function argument + scope + variable.parameter + settings + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + bold + foreground + #268bd2 + + + + name + Tag start/end + scope + punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end + settings + + foreground + #93A1A1 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + foreground + #93A1A1 + + + + name + Library function + scope + support.function + settings + + foreground + #268bd2 + + + + name + Continuation + scope + punctuation.separator.continuation + settings + + foreground + #dc322f + + + + name + Library constant + scope + support.constant + settings + + + + name + Library class/type + scope + support.type, support.class + settings + + foreground + #859900 + + + + name + Library Exception + scope + support.type.exception + settings + + foreground + #CB4B16 + + + + name + Special + scope + keyword.other.special-method + settings + + foreground + #CB4B16 + + + + name + Library variable + scope + support.other.variable + settings + + + + name + Invalid + scope + invalid + settings + + + + name + Quoted String + scope + string.quoted.double, string.quoted.single + settings + + foreground + #2aa198 + + + + name + Quotes + scope + punctuation.definition.string.begin, punctuation.definition.string.end + settings + + foreground + #dc322f + + + + name + CSS: Property + scope + entity.name.tag.css, support.type.property-name.css, meta.property-name.css + settings + + fontStyle + + foreground + #b58900 + + + + name + CSS: @font-face + scope + source.css + settings + + foreground + #dc322f + + + + name + CSS: Selector + scope + meta.selector.css + settings + + fontStyle + + foreground + #586e75 + + + + name + CSS: {} + scope + punctuation.section.property-list.css + settings + + foreground + #6c71c4 + + + + name + CSS: Numeric Value + scope + meta.property-value.css constant.numeric.css, keyword.other.unit.css,constant.other.color.rgb-value.css + settings + + fontStyle + + foreground + #2aa198 + + + + name + CSS: Value + scope + meta.property-value.css + settings + + fontStyle + + foreground + #2aa198 + + + + name + CSS: !Important + scope + keyword.other.important.css + settings + + foreground + #dc322f + + + + name + CSS: Standard Value + scope + support.constant.color + settings + + foreground + #2aa198 + + + + name + CSS: Tag + scope + entity.name.tag.css + settings + + foreground + #859900 + + + + name + CSS: : , + scope + punctuation.separator.key-value.css, punctuation.terminator.rule.css + settings + + fontStyle + + foreground + #586e75 + + + + name + CSS .class + scope + entity.other.attribute-name.class.css + settings + + fontStyle + + foreground + #268bd2 + + + + name + CSS :pseudo + scope + entity.other.attribute-name.pseudo-element.css, entity.other.attribute-name.pseudo-class.css + settings + + fontStyle + + foreground + #cb4b16 + + + + name + CSS: #id + scope + entity.other.attribute-name.id.css + settings + + fontStyle + + foreground + #268bd2 + + + + name + JS: Function Name + scope + meta.function.js, entity.name.function.js, support.function.dom.js + settings + + foreground + #b58900 + + + + name + JS: Source + scope + text.html.basic source.js.embedded.html + settings + + fontStyle + + foreground + #b58900 + + + + name + JS: Function + scope + storage.type.function.js + settings + + foreground + #268bd2 + + + + name + JS: Numeric Constant + scope + constant.numeric.js + settings + + foreground + #2aa198 + + + + name + JS: [] + scope + meta.brace.square.js + settings + + foreground + #268bd2 + + + + name + JS: Storage Type + scope + storage.type.js + settings + + foreground + #268bd2 + + + + name + () + scope + meta.brace.round, punctuation.definition.parameters.begin.js, punctuation.definition.parameters.end.js + settings + + foreground + #93A1A1 + + + + name + {} + scope + meta.brace.curly.js + settings + + foreground + #268bd2 + + + + name + HTML: Doctype + scope + entity.name.tag.doctype.html, meta.tag.sgml.html, string.quoted.double.doctype.identifiers-and-DTDs.html + settings + + fontStyle + italic + foreground + #93a1a1 + + + + name + HTML: Comment Block + scope + comment.block.html + settings + + fontStyle + italic + foreground + #839496 + + + + name + HTML: Script + scope + entity.name.tag.script.html + settings + + fontStyle + italic + + + + name + HTML: Style + scope + source.css.embedded.html string.quoted.double.html + settings + + fontStyle + + foreground + #2aa198 + + + + name + HTML: Text + scope + text.html.ruby + settings + + fontStyle + bold + foreground + #cb4b16 + + + + name + HTML: = + scope + text.html.basic meta.tag.other.html, text.html.basic meta.tag.any.html, text.html.basic meta.tag.block.any, text.html.basic meta.tag.inline.any, text.html.basic meta.tag.structure.any.html, text.html.basic source.js.embedded.html, punctuation.separator.key-value.html + settings + + fontStyle + + foreground + #657b83 + + + + name + HTML: something= + scope + text.html.basic entity.other.attribute-name.html + settings + + foreground + #657b83 + + + + name + HTML: " + scope + text.html.basic meta.tag.structure.any.html punctuation.definition.string.begin.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html + settings + + fontStyle + + foreground + #2aa198 + + + + name + HTML: <tag> + scope + entity.name.tag.block.any.html + settings + + fontStyle + bold + foreground + #268bd2 + + + + name + HTML: style + scope + source.css.embedded.html entity.name.tag.style.html + settings + + fontStyle + italic + + + + name + HTML: <style> + scope + entity.name.tag.style.html + settings + + fontStyle + + + + + name + HTML: {} + scope + text.html.basic punctuation.section.property-list.css + settings + + fontStyle + + + + + name + HTML: Embeddable + scope + source.css.embedded.html, comment.block.html + settings + + fontStyle + italic + foreground + #839496 + + + + name + Ruby: Variable definition + scope + punctuation.definition.variable.ruby + settings + + fontStyle + + foreground + #268bd2 + + + + name + Ruby: Function Name + scope + meta.function.method.with-arguments.ruby + settings + + foreground + #657b83 + + + + name + Ruby: Variable + scope + variable.language.ruby + settings + + foreground + #2aa198 + + + + name + Ruby: Function + scope + entity.name.function.ruby + settings + + foreground + #268bd2 + + + + name + Ruby: Keyword Control + scope + keyword.control.ruby, keyword.control.def.ruby + settings + + fontStyle + bold + foreground + #859900 + + + + name + Ruby: Class + scope + keyword.control.class.ruby, meta.class.ruby + settings + + foreground + #859900 + + + + name + Ruby: Class Name + scope + entity.name.type.class.ruby + settings + + fontStyle + + foreground + #b58900 + + + + name + Ruby: Keyword + scope + keyword.control.ruby + settings + + fontStyle + + foreground + #859900 + + + + name + Ruby: Support Class + scope + support.class.ruby + settings + + fontStyle + + foreground + #b58900 + + + + name + Ruby: Special Method + scope + keyword.other.special-method.ruby + settings + + foreground + #859900 + + + + name + Ruby: Constant + scope + constant.language.ruby, constant.numeric.ruby + settings + + foreground + #2aa198 + + + + name + Ruby: Constant Other + scope + variable.other.constant.ruby + settings + + fontStyle + + foreground + #b58900 + + + + name + Ruby: :symbol + scope + constant.other.symbol.ruby + settings + + fontStyle + + foreground + #2aa198 + + + + name + Ruby: Punctuation Section '' + scope + punctuation.section.embedded.ruby, punctuation.definition.string.begin.ruby, punctuation.definition.string.end.ruby + settings + + foreground + #dc322f + + + + name + Ruby: Special Method + scope + keyword.other.special-method.ruby + settings + + foreground + #cb4b16 + + + + name + PHP: Include + scope + keyword.control.import.include.php + settings + + foreground + #cb4b16 + + + + name + Ruby: erb = + scope + text.html.ruby meta.tag.inline.any.html + settings + + fontStyle + + foreground + #839496 + + + + name + Ruby: erb "" + scope + text.html.ruby punctuation.definition.string.begin, text.html.ruby punctuation.definition.string.end + settings + + fontStyle + + foreground + #2aa198 + + + + name + PHP: Quoted Single + scope + punctuation.definition.string.begin, punctuation.definition.string.end + settings + + foreground + #839496 + + + + name + PHP: [] + scope + keyword.operator.index-start.php, keyword.operator.index-end.php + settings + + foreground + #dc322f + + + + name + PHP: Array + scope + meta.array.php + settings + + foreground + #586e75 + + + + name + PHP: Array() + scope + meta.array.php support.function.construct.php, meta.array.empty.php support.function.construct.php + settings + + fontStyle + + foreground + #b58900 + + + + name + PHP: Array Construct + scope + support.function.construct.php + settings + + foreground + #b58900 + + + + name + PHP: Array Begin + scope + punctuation.definition.array.begin, punctuation.definition.array.end + settings + + foreground + #dc322f + + + + name + PHP: Numeric Constant + scope + constant.numeric.php + settings + + foreground + #2aa198 + + + + name + PHP: New + scope + keyword.other.new.php + settings + + foreground + #CB4B16 + + + + name + PHP: :: + scope + support.class.php, keyword.operator.class + settings + + fontStyle + + foreground + #586e75 + + + + name + PHP: Other Property + scope + variable.other.property.php + settings + + foreground + #93a1a1 + + + + name + PHP: Class + scope + storage.modifier.extends.php, storage.type.class.php, keyword.operator.class.php + settings + + foreground + #b58900 + + + + name + PHP: Class Function + settings + + + + name + PHP: Inherited Class + scope + meta.other.inherited-class.php + settings + + fontStyle + + foreground + #586e75 + + + + name + PHP: Storage Type + scope + storage.type.php + settings + + foreground + #859900 + + + + name + PHP: Function + scope + entity.name.function.php + settings + + foreground + #93a1a1 + + + + name + PHP: Function Construct + scope + support.function.construct.php + settings + + foreground + #859900 + + + + name + PHP: Function Call + scope + entity.name.type.class.php, meta.function-call.php, meta.function-call.static.php, meta.function-call.object.php + settings + + foreground + #839496 + + + + name + PHP: Comment + scope + keyword.other.phpdoc + settings + + fontStyle + + foreground + #93a1a1 + + + + name + PHP: Source Emebedded + scope + source.php.embedded.block.html + settings + + foreground + #cb4b16 + + + + name + PHP: Storage Type Function + scope + storage.type.function.php + settings + + foreground + #cb4b16 + + + + name + C: constant + scope + constant.numeric.c + settings + + fontStyle + + foreground + #2aa198 + + + + name + C: Meta Preprocessor + scope + meta.preprocessor.c.include, meta.preprocessor.macro.c + settings + + fontStyle + + foreground + #cb4b16 + + + + name + C: Keyword + scope + keyword.control.import.define.c, keyword.control.import.include.c + settings + + fontStyle + + foreground + #cb4b16 + + + + name + C: Function Preprocessor + scope + entity.name.function.preprocessor.c + settings + + fontStyle + + foreground + #cb4b16 + + + + name + C: include <something.c> + scope + meta.preprocessor.c.include string.quoted.other.lt-gt.include.c, meta.preprocessor.c.include punctuation.definition.string.begin.c, meta.preprocessor.c.include punctuation.definition.string.end.c + settings + + fontStyle + + foreground + #2aa198 + + + + name + C: Function + scope + support.function.C99.c, support.function.any-method.c, entity.name.function.c + settings + + fontStyle + + foreground + #586e75 + + + + name + C: " + scope + punctuation.definition.string.begin.c, punctuation.definition.string.end.c + settings + + fontStyle + + foreground + #2aa198 + + + + name + C: Storage Type + scope + storage.type.c + settings + + fontStyle + + foreground + #b58900 + + + + name + diff: header + scope + meta.diff, meta.diff.header + settings + + background + #b58900 + fontStyle + italic + foreground + #E0EDDD + + + + name + diff: deleted + scope + markup.deleted + settings + + background + #eee8d5 + fontStyle + + foreground + #dc322f + + + + name + diff: changed + scope + markup.changed + settings + + background + #eee8d5 + fontStyle + + foreground + #cb4b16 + + + + name + diff: inserted + scope + markup.inserted + settings + + background + #eee8d5 + foreground + #219186 + + + + name + Markdown: Linebreak + scope + text.html.markdown meta.dummy.line-break + settings + + background + #A57706 + foreground + #E0EDDD + + + + name + Markdown: Raw + scope + text.html.markdown markup.raw.inline + settings + + foreground + #2aa198 + + + + name + reST raw + scope + text.restructuredtext markup.raw + settings + + foreground + #2aa198 + + + + name + Other: Removal + scope + other.package.exclude, other.remove + settings + + fontStyle + + foreground + #dc322f + + + + name + Other: Add + scope + other.add + settings + + foreground + #2aa198 + + + + name + Tex: {} + scope + punctuation.section.group.tex , punctuation.definition.arguments.begin.latex, punctuation.definition.arguments.end.latex, punctuation.definition.arguments.latex + settings + + fontStyle + + foreground + #dc322f + + + + name + Tex: {text} + scope + meta.group.braces.tex + settings + + fontStyle + + foreground + #b58900 + + + + name + Tex: Other Math + scope + string.other.math.tex + settings + + fontStyle + + foreground + #b58900 + + + + name + Tex: {var} + scope + variable.parameter.function.latex + settings + + fontStyle + + foreground + #cb4b16 + + + + name + Tex: Math \\ + scope + punctuation.definition.constant.math.tex + settings + + fontStyle + + foreground + #dc322f + + + + name + Tex: Constant Math + scope + text.tex.latex constant.other.math.tex, constant.other.general.math.tex, constant.other.general.math.tex, constant.character.math.tex + settings + + fontStyle + + foreground + #2aa198 + + + + name + Tex: Other Math String + scope + string.other.math.tex + settings + + fontStyle + + foreground + #b58900 + + + + name + Tex: $ + scope + punctuation.definition.string.begin.tex, punctuation.definition.string.end.tex + settings + + fontStyle + + foreground + #dc322f + + + + name + Tex: \label + scope + keyword.control.label.latex, text.tex.latex constant.other.general.math.tex + settings + + fontStyle + + foreground + #2aa198 + + + + name + Tex: \label { } + scope + variable.parameter.definition.label.latex + settings + + fontStyle + + foreground + #dc322f + + + + name + Tex: Function + scope + support.function.be.latex + settings + + fontStyle + + foreground + #859900 + + + + name + Tex: Support Function Section + scope + support.function.section.latex + settings + + fontStyle + + foreground + #cb4b16 + + + + name + Tex: Support Function + scope + support.function.general.tex + settings + + fontStyle + + foreground + #2aa198 + + + + name + Tex: Comment + scope + punctuation.definition.comment.tex, comment.line.percentage.tex + settings + + fontStyle + italic + + + + name + Tex: Reference Label + scope + keyword.control.ref.latex + settings + + fontStyle + + foreground + #2aa198 + + + + name + Python: docstring + scope + string.quoted.double.block.python + settings + + fontStyle + + foreground + #586e75 + + + + name + Python: storage + scope + storage.type.class.python, storage.type.function.python, storage.modifier.global.python + settings + + fontStyle + + foreground + #859900 + + + + name + Python: import + scope + keyword.control.import.python, keyword.control.import.from.python + settings + + foreground + #cb4b16 + + + + name + Python: Support.exception + scope + support.type.exception.python + settings + + foreground + #b58900 + + + + name + Shell: builtin + scope + support.function.builtin.shell + settings + + foreground + #859900 + + + + name + Shell: variable + scope + variable.other.normal.shell + settings + + foreground + #cb4b16 + + + + name + Shell: DOT_FILES + scope + source.shell + settings + + fontStyle + + foreground + #268bd2 + + + + name + Shell: meta scope in loop + scope + meta.scope.for-in-loop.shell, variable.other.loop.shell + settings + + fontStyle + + foreground + #586e75 + + + + name + Shell: "" + scope + punctuation.definition.string.end.shell, punctuation.definition.string.begin.shell + settings + + fontStyle + + foreground + #859900 + + + + name + Shell: Meta Block + scope + meta.scope.case-block.shell, meta.scope.case-body.shell + settings + + fontStyle + + foreground + #586e75 + + + + name + Shell: [] + scope + punctuation.definition.logical-expression.shell + settings + + fontStyle + + foreground + #dc322f + + + + name + Shell: Comment + scope + comment.line.number-sign.shell + settings + + fontStyle + italic + + + + name + Java: import + scope + keyword.other.import.java + settings + + fontStyle + + foreground + #cb4b16 + + + + name + Java: meta-import + scope + storage.modifier.import.java + settings + + fontStyle + + foreground + #586E75 + + + + name + Java: Class + scope + meta.class.java storage.modifier.java + settings + + fontStyle + + foreground + #b58900 + + + + name + Java: /* comment */ + scope + source.java comment.block + settings + + fontStyle + + foreground + #586e75 + + + + name + Java: /* @param */ + scope + comment.block meta.documentation.tag.param.javadoc keyword.other.documentation.param.javadoc + settings + + fontStyle + + foreground + #586e75 + + + + name + Perl: variables + scope + punctuation.definition.variable.perl, variable.other.readwrite.global.perl, variable.other.predefined.perl, keyword.operator.comparison.perl + settings + + foreground + #b58900 + + + + name + Perl: functions + scope + support.function.perl + settings + + foreground + #859900 + + + + name + Perl: comments + scope + comment.line.number-sign.perl + settings + + fontStyle + italic + foreground + #586E75 + + + + name + Perl: quotes + scope + punctuation.definition.string.begin.perl, punctuation.definition.string.end.perl + settings + + foreground + #2aa198 + + + + name + Perl: \char + scope + constant.character.escape.perl + settings + + foreground + #DC322F + + + + + name + Markdown: Headings + scope + markup.heading.markdown, markup.heading.1.markdown, markup.heading.2.markdown, markup.heading.3.markdown, markup.heading.4.markdown, markup.heading.5.markdown, markup.heading.6.markdown + settings + + foreground + #268bd2 + + + + name + Markdown: Bold + scope + markup.bold.markdown + settings + + fontStyle + bold + foreground + #586E75 + + + + name + Markdown: Italic + scope + markup.italic.markdown + settings + + fontStyle + italic + foreground + #586E75 + + + + name + Markdown: Punctuation for Bold, Italic, and Inline Block + scope + punctuation.definition.bold.markdown, punctuation.definition.italic.markdown, punctuation.definition.raw.markdown + settings + + foreground + #DC322F + + + + name + Markdown: Bulleted List + scope + markup.list.unnumbered.markdown + settings + + foreground + #B58900 + + + + name + Markdown: Numbered List + scope + markup.list.numbered.markdown + settings + + foreground + #859900 + + + + name + Markdown: Block and Inline Block + scope + markup.raw.block.markdown, markup.raw.inline.markdown + settings + + foreground + #2aa198 + + + + name + markup.quote.markdown + scope + markup.quote.markdown + settings + + foreground + #6c71c4 + + + + + name + punctuation.definition.blockquote.markdown + scope + punctuation.definition.blockquote.markdown + settings + + foreground + #6c71c4 + + + + + name + Markdown: Seperator + scope + meta.separator.markdown + settings + + foreground + #d33682 + + + + + name + Markdown: Link URL, Reference + scope + markup.underline.link.markdown + settings + + foreground + #839496 + + + + + name + Markdown: Link Title + scope + markup.underline.link.markdown + settings + + foreground + #839496 + + + + + name + Markdown: Link Punctuation + scope + meta.link.inet.markdown, meta.link.email.lt-gt.markdown, punctuation.definition.string.begin.markdown, punctuation.definition.string.end.markdown, punctuation.definition.link.markdown + settings + + foreground + #DC322F + + + + + name + text plain + scope + text.plain + settings + + foreground + #6a8187 + + + + name + SublimeLinter Annotations + scope + sublimelinter.notes + settings + + background + #eee8d5 + foreground + #eee8d5 + + + + name + SublimeLinter Error Outline + scope + sublimelinter.outline.illegal + settings + + background + #93a1a1 + foreground + #93a1a1 + + + + name + SublimeLinter Error Underline + scope + sublimelinter.underline.illegal + settings + + background + #dc322f + + + + name + SublimeLinter Warning Outline + scope + sublimelinter.outline.warning + settings + + background + #839496 + foreground + #839496 + + + + name + SublimeLinter Warning Underline + scope + sublimelinter.underline.warning + settings + + background + #b58900 + + + + name + SublimeLinter Violation Outline + scope + sublimelinter.outline.violation + settings + + background + #657b83 + foreground + #657b83 + + + + name + SublimeLinter Violation Underline + scope + sublimelinter.underline.violation + settings + + background + #cb4b16 + + + + + uuid + 38E819D9-AE02-452F-9231-ECC3B204AFD7 + colorSpaceName + sRGB + + diff --git a/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage new file mode 100644 index 0000000..bb91fa2 --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage @@ -0,0 +1,1522 @@ + + + + + fileTypes + + swift + + firstLineMatch + ^#!/.*\bswift + keyEquivalent + ^~S + name + Swift + patterns + + + include + #shebang-line + + + include + #comment + + + include + #attribute + + + include + #literal + + + include + #operator + + + include + #declaration + + + include + #storage-type + + + include + #keyword + + + include + #type + + + include + #builtin-global-function + + + include + #builtin-function + + + include + #builtin-property + + + include + #builtin-static-property + + + include + #builtin-constant + + + include + #section-punctuation + + + repository + + arithmetic-operator + + match + (?<![/=\-+!*%<>&|\^~.])(\+|\-|\*|\/)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.arithmetic.swift + + array-type + + begin + \b(Array)(\[) + beginCaptures + + 1 + + name + support.type.array.swift + + 2 + + name + punctuation.definition.array.begin.swift + + + end + (\]) + endCaptures + + 1 + + name + punctuation.definition.array.end.swift + + + name + meta.array.swift + patterns + + + include + $self + + + + assignment-operator + + match + (?<![/=\-+!*%<>&|\^~.])(\+|\-|\*|\/|%|<<|>>|&|\^|\||&&|\|\|)?=(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.assignment.swift + + attribute + + comment + attribute + name + meta.attribute.swift + patterns + + + begin + ((@)(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B))(\() + beginCaptures + + 1 + + name + storage.modifier.attribute.swift + + 2 + + name + punctuation.definition.attribute.swift + + 3 + + name + punctuation.definition.attribute-arguments.begin.swift + + + contentName + meta.attribute.arguments.swift + end + \) + endCaptures + + 0 + + name + punctuation.definition.attribute-arguments.end.swift + + + patterns + + + include + $self + + + + + captures + + 1 + + name + storage.modifier.attribute.swift + + 2 + + name + punctuation.definition.attribute.swift + + + match + ((@)(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B)) + + + + bitwise-operator + + match + (?<![/=\-+!*%<>&|\^~.])(&|\||\^|<<|>>)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.bitwise.swift + + boolean-literal + + match + \b(true|false)\b + name + constant.language.boolean.swift + + branch-statement-keyword + + name + keyword.control.branch.swift + patterns + + + include + #if-statement-keyword + + + include + #switch-statement-keyword + + + + builtin-class-type + + comment + Builtin class types + match + \b(Managed(Buffer|ProtoBuffer)|NonObjectiveCBase|AnyGenerator)\b + name + support.class.swift + + builtin-constant + + comment + Process is really an enum, but it acts like a constant + match + \bProcess\b + name + support.constant.swift + + builtin-enum-type + + comment + Builtin enum types + match + \b(MirrorDisposition|Bit|ImplicitlyUnwrappedOptional|Optional|UnicodeDecodingResult|QuickLookObject|FloatingPointClassification)\b + name + support.type.swift + + builtin-function + + comment + Functions provided in the standard library; found by searching ^[ \t]+func\s+(\w+?)[<(] + match + (?<=\.)(s(tartsWith|ort|u(ccessor|perclassMirror|btract)|amePositionIn)|has(Suffix|Prefix)|next(Object)?|c(haracterAtIndex|o(ntains|untByEnumeratingWithState|pyWithZone)|ustom(Mirror|PlaygroundQuickLook)|lamp)|t(o(IntMax|Opaque|UIntMax)|ake(RetainedValue|UnretainedValue))|i(s(S(trictSu(persetOf|bsetOf)|u(persetOf|bsetOf))|DisjointWith|EmptyInput|ASCII)|n(tersect|itialize(From)?|dex(Of|ForKey)))|object(Enumerator|ForKey|AtIndex)|d(istanceTo|e(s(cendant|troy)|alloc))|un(ion|derestimateCount)|join|p(ut|redecessor)|e(scape|numerate|lementsEqual|xclusiveOr)|keyEnumerator|f(ilter|latMap)|w(ith(CString|U(nsafe(MutablePointer(s|To(Elements|Value))|BufferPointer)|TF8Buffer))|riteTo)|le(ngth|xicographicalCompare)|a(ssign(BackwardFrom|From)|dvancedBy|utorelease)|re(tain|duce|verse|questNativeBuffer|lease)|ge(nerate|t(Mirror|Objects))|m(inElement|ove(Initialize(BackwardFrom|From)|AssignFrom)?|ember|a(p|xElement)))\b + name + support.function.swift + + builtin-global-function + + comment + Global functions provided in the standard library; found by searching ^func\s+(\w+?)[<(] + match + \b(s(tride(of(Value)?)?|izeof(Value)?|ort|uffix|pli(ce|t)|wap)|numericCast|transcode|i(sUniquelyReferenced(NonObjC)?|nsert)|zip|overlaps|d(istance|ump|ebugPrint|rop(First|Last))|unsafe(BitCast|Downcast|Unwrap|AddressOf)|join|pr(int|e(condition|fix))|extend|with(Unsafe(MutablePointer(s)?|Pointer(s)?)|ExtendedLifetime|VaList)|lazy|a(ssert(ionFailure)?|nyGenerator|dvance|lignof(Value)?|bs)|re(flect|adLine|move(Range|Last|A(tIndex|ll)))|getVaList|m(in|ax))\b + name + support.function.swift + + builtin-property + + comment + Properties provided in the standard library; found by searching ^\s+var\s+(\w+?)\W + match + (?<=\.)(s(t(art(Index)?|ringValue)|ummary)|has(hValue|PointerRepresentation)|nulTerminatedUTF8|c(haracters|ount|apacity)|i(s(S(ign(Minus|aling)|ubnormal)|N(ormal|aN)|Infinite|Zero|Empty|Finite|ASCII)|ndices|dentity)|o(wner|bjectIdentifier)|d(isposition|e(scription|bugDescription))|u(nicodeScalar(s)?|tf(16|8(Start)?)|intValue|ppercaseString)|end(Index)?|value(s|Type)?|keys|quickLookObject|f(irst|loatingPointClass)|l(ittleEndian|owercaseString|ast)|a(llocatedElementCount|rray)|rawValue|memory|b(yteS(ize|wapped)|igEndian|oolValue|uffer|aseAddress))\b + name + support.variable.swift + + builtin-protocol-type + + comment + Builtin protocol types + match + \b(Ra(n(domAccessIndexType|geReplaceableCollectionType)|wRepresentable)|GeneratorType|M(irror(Type|PathType)|utable(Sliceable|CollectionType))|B(i(twiseOperationsType|directionalIndexType)|oolean(Type|LiteralConvertible))|S(tr(i(ng(InterpolationConvertible|LiteralConvertible)|deable)|eamable)|i(nkType|gned(NumberType|IntegerType))|e(tAlgebraType|quenceType)|liceable)|Hashable|NilLiteralConvertible|C(o(llectionType|mparable)|ustom(Reflectable|StringConvertible|DebugStringConvertible|PlaygroundQuickLookable|LeafReflectable)|VarArgType)|Inte(rvalType|ger(Type|LiteralConvertible|ArithmeticType))|O(utputStreamType|ptionSetType)|DictionaryLiteralConvertible|Un(signedIntegerType|icode(ScalarLiteralConvertible|CodecType))|E(quatable|rrorType|xten(sibleCollectionType|dedGraphemeClusterLiteralConvertible))|F(orwardIndexType|loat(ingPointType|LiteralConvertible))|A(ny(CollectionType|Object)|rrayLiteralConvertible|bsoluteValuable))\b + name + support.type.swift + + builtin-static-property + + comment + Static properties provided in the standard library; found by searching ^\s*(class|static)\s+var\s+(\w+?)\W + match + (?<=\.)(infinity|NaN|quietNaN|allZeros|m(in|ax))\b|(?<=\bProcess\.)(arguments|argc|unsafeArgv)\b + name + support.variable.swift + + builtin-struct-type + + comment + Builtin struct types + match + \b(R(e(peat|verse(RandomAccess(Collection|Index)|Collection|Index))|a(nge(Generator)?|wByte))|Generator(Sequence|OfOne)|M(irror|a(nagedBufferPointer|p(Generator|Sequence|Collection)))|Bool|S(t(aticString|ri(ng|deT(hrough(Generator)?|o(Generator)?)))|inkOf|et(Generator|Index)?)|HalfOpenInterval|C(haracter|o(ntiguousArray|llectionOfOne)|OpaquePointer|losedInterval|VaListPointer)|In(t(16|8|32|64)?|dexingGenerator)|Zip2(Generator|Sequence)|ObjectIdentifier|D(ictionary(Generator|Index|Literal)?|ouble)|U(n(safe(Mutable(BufferPointer|Pointer)|BufferPointer(Generator)?|Pointer)|icodeScalar|managed)|TF(16|8|32)|Int(16|8|32|64)?)|PermutationGenerator|E(numerate(Generator|Sequence)|mpty(Generator|Collection))|F(ilter(Generator|Sequence|Collection(Index)?)|loat(80)?)|Lazy(RandomAccessCollection|BidirectionalCollection|Sequence|ForwardCollection)|A(ny(RandomAccess(Collection|Index)|Bidirectional(Collection|Index)|Sequence|Forward(Collection|Index))|utoreleasingUnsafeMutablePointer|rray(Slice)?))\b + name + support.type.swift + + builtin-type + + comment + Types provided in the standard library; found by searching ^(@objc\s+)?(struct|protocol|class|enum)\s+(\w+)[\s<].+$ + patterns + + + include + #builtin-class-type + + + include + #builtin-enum-type + + + include + #builtin-protocol-type + + + include + #builtin-struct-type + + + include + #builtin-typealias + + + + builtin-typealias + + comment + Builtin typealiases + match + \b(BooleanLiteralType|StringLiteralType|C(Bool|S(hort|ignedChar)|Char(16|32)?|Int|Double|Unsigned(Short|Char|Int|Long(Long)?)|Float|WideChar|Long(Long)?)|Int(Max|egerLiteralType)|U(nicodeScalarType|IntMax|Word)|PlaygroundQuickLook|ExtendedGraphemeClusterType|Void|Float(32|LiteralType|64)|Word|Any(Class)?)\b + name + support.type.swift + + capture-specifier + + comment + matches weak, unowned, unowned(safe), unowned(unsafe) + match + \b(weak|unowned(?:\((?:un)?safe\))?) + name + keyword.other.capture-specifier.swift + + coalescing-operator + + match + (?<![?/=\-+!*%<>&|\^~.])\?\?(?![?/=\-+!*%<>&|\^~.]) + name + keyword.operator.coalescing.swift + + code-block + + begin + (\{) + beginCaptures + + 1 + + name + punctuation.definition.code-block.begin.swift + + + comment + code-block + end + (\}) + endCaptures + + 1 + + name + punctuation.definition.code-block.end.swift + + + patterns + + + include + $self + + + + collection-type + + comment + Collection types + patterns + + + include + #array-type + + + include + #dictionary-type + + + + comment + + comment + All comment types + patterns + + + include + #block-doc + + + include + #block + + + include + #inline-doc + + + include + #inline + + + repository + + block + + begin + /\* + beginCaptures + + 0 + + name + punctuation.definition.comment.block.begin.swift + + + end + \*/ + endCaptures + + 0 + + name + punctuation.definition.comment.block.end.swift + + + name + comment.block.swift + patterns + + + include + #nested + + + repository + + nested + + begin + /\* + end + \*/ + patterns + + + include + #block-nested + + + + + + block-doc + + begin + /\*\* + beginCaptures + + 0 + + name + punctuation.definition.comment.block.documentation.begin.swift + + + end + \*/ + endCaptures + + 0 + + name + punctuation.definition.comment.block.documentation.end.swift + + + name + comment.block.documentation.swift + patterns + + + include + #nested + + + include + text.restructuredtext#tags + + + repository + + nested + + begin + /\* + end + \*/ + patterns + + + include + #block-nested + + + include + text.restructuredtext#tags + + + + + + inline + + begin + (^[ \t]+)?(?=//) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.swift + + + end + (?!\G) + patterns + + + begin + // + beginCaptures + + 0 + + name + punctuation.definition.comment.swift + + + end + \n + name + comment.line.double-slash.swift + + + + inline-doc + + begin + (^[ \t]+)?(?=///) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.swift + + + end + (?!\G) + patterns + + + begin + /// + beginCaptures + + 0 + + name + punctuation.definition.comment.swift + + + end + \n + name + comment.line.triple-slash.swift + + + + + + comparative-operator + + match + (?<![/=\-+!*%<>&|\^~.])((=|!)==?|(<|>)=?|~=)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.comparative.swift + + control-transfer-statement-keyword + + comment + control-transfer-statement + match + \b(continue|break|fallthrough|return)\b + name + keyword.control.transfer.swift + + custom-operator + + patterns + + + match + (?<=[\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?![\s)\]},;:]) + name + keyword.operator.custom.prefix.unary.swift + + + match + (?<![\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?![\s)\]},;:\.]) + name + keyword.operator.custom.postfix.unary.swift + + + match + (?<=[\s(\[{,;:])([/=\-+!*%<>&|\^~.]++)(?=[\s)\]},;:]) + name + keyword.operator.custom.binary.swift + + + + declaration + + comment + declaration + name + meta.declaration.swift + patterns + + + include + #import-declaration + + + include + #function-declaration + + + + declaration-specifier + + comment + declaration-specifier + match + \b(class|deinit|enum|extension|func|import|init|inout|internal|let|operator|private|protocol|public|static|struct|subscript|typealias|var)\b + name + keyword.other.declaration-specifier.swift + + dictionary-type + + begin + \b(Dictionary)(\[) + beginCaptures + + 1 + + name + support.type.dictionary.swift + + 2 + + name + punctuation.definition.dictionary.begin.swift + + + end + (\]) + endCaptures + + 1 + + name + punctuation.definition.dictionary.end.swift + + + name + meta.dictionary.swift + patterns + + + include + $self + + + + floating-point-literal + + name + constant.numeric.floating-point.swift + patterns + + + comment + floating-point-literal -> (decimal-literal)(decimal-fraction)?(decimal-exponent)? + match + \b([0-9][0-9_]*)(\.([0-9][0-9_]*))?([eE][+\-]?([0-9][0-9_]*))?\b + + + comment + floating-point-literal -> (hexadecimal-literal)(hexadecimal-fraction)?(hexadecimal-exponent) + match + \b(0x\h[\h_]*)(\.(0x\h[\h_]*))?([pP][+\-]?(0x\h[\h_]*))\b + + + + function-declaration + + begin + \b(func)\s+(\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+)\s*(?=\(|<) + beginCaptures + + 1 + + name + storage.type.function.swift + + 2 + + name + entity.name.function.swift + + + comment + function-declaration + end + (?<=\})|$ + name + meta.function-declaration.swift + patterns + + + include + #generic-parameter-clause + + + include + #parameter-clause + + + include + #function-result + + + begin + (\{) + beginCaptures + + 1 + + name + punctuation.section.function.begin.swift + + + end + (\}) + endCaptures + + 1 + + name + punctuation.section.function.end.swift + + + name + meta.function-declaration.body.swift + patterns + + + include + $self + + + + + + function-result + + begin + (?<![/=\-+!*%<>&|\^~.])(\->)(?![/=\-+!*%<>&|\^~.])\s* + beginCaptures + + 1 + + name + keyword.operator.function-result.swift + + + comment + function-result + end + (?=\{)|$ + name + meta.function-result.swift + patterns + + + include + #type + + + + generic-parameter-clause + + begin + (<) + beginCaptures + + 1 + + name + punctuation.definition.generic-parameter-clause.begin.swift + + + comment + generic-parameter-clause + end + (>) + endCaptures + + 1 + + name + punctuation.definition.generic-parameter-clause.end.swift + + + name + meta.generic-parameter-clause.swift + patterns + + + include + $self + + + + identifier + + comment + identifier + match + (\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B) + name + meta.identifier.swift + + if-statement-keyword + + comment + if-statement + match + \b(if|else)\b + name + keyword.control.if.swift + + import-declaration + + captures + + 1 + + name + keyword.other.import.swift + + 2 + + name + storage.modifier.swift + + 3 + + name + support.type.module.import.swift + + + comment + import-declaration + match + \b(import)\s+(?:(typealias|struct|class|enum|protocol|var|func)\s+)?((?:\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+)(?:\.(?:\B\$[0-9]+|\b[\w^\d][\w\d]*\b|\B`[\w^\d][\w\d]*`\B|[/=\-+!*%<>&|\^~.]+))*) + name + meta.import.swift + + increment-decrement-operator + + match + (?<![/=\-+!*%<>&|\^~.])(\+\+|\-\-)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.increment-or-decrement.swift + + integer-literal + + name + constant.numeric.integer.swift + patterns + + + comment + binary-literal + match + (\B\-|\b)(0b[01][01_]*)\b + name + constant.numeric.integer.binary.swift + + + comment + octal-literal + match + (\B\-|\b)(0o[0-7][0-7_]*)\b + name + constant.numeric.integer.octal.swift + + + comment + decimal-literal + match + (\B\-|\b)([0-9][0-9_]*)\b + name + constant.numeric.integer.decimal.swift + + + comment + hexadecimal-literal + match + (\B\-|\b)(0x\h[\h_]*)\b + name + constant.numeric.integer.hexadecimal.swift + + + + keyword + + patterns + + + include + #branch-statement-keyword + + + include + #control-transfer-statement-keyword + + + include + #loop-statement-keyword + + + include + #declaration-specifier + + + include + #capture-specifier + + + include + #operator-declaration-keyword + + + comment + declaration keyword + match + \b(class|deinit|enum|extension|func|import|init|inout|internal|let|operator|private|protocol|public|static|struct|subscript|typealias|var)\b + name + storage.type.declaration.swift + + + comment + statement keyword + match + \b(break|case|continue|default|defer|do|else|fallthrough|for|guard|if|in|repeat|return|switch|where|while)\b + name + keyword.control.statement.swift + + + comment + expression and type keyword + match + \b(as|catch|dynamicType|false|is|nil|rethrows|super|self|Self|throw|throws|true|try|__COLUMN__|__FILE__|__FUNCTION__|__LINE__)\b + name + keyword.other.statement.swift + + + comment + other keyword + match + \b(associativity|convenience|dynamic|didSet|final|get|infix|indirect|lazy|left|mutating|none|nonmutating|optional|override|postfix|precedence|prefix|Protocol|required|right|set|Type|unowned|weak|willSet)\b + name + keyword.other.swift + + + + literal + + patterns + + + include + #integer-literal + + + include + #floating-point-literal + + + include + #string-literal + + + include + #special-literal + + + include + #boolean-literal + + + include + #nil-literal + + + + logical-operator + + match + (?<![/=\-+!*%<>&|\^~.])(!|&&|\|\|)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.logical.swift + + loop-statement-keyword + + comment + loop-statement + match + \b(while|do|for|in)\b + name + keyword.control.loop.swift + + nil-literal + + match + \bnil\b + name + constant.language.nil.swift + + operator + + patterns + + + include + #comparative-operator + + + include + #assignment-operator + + + include + #logical-operator + + + include + #remainder-operator + + + include + #increment-decrement-operator + + + include + #overflow-operator + + + include + #range-operator + + + include + #bitwise-operator + + + include + #arithmetic-operator + + + include + #ternary-operator + + + include + #type-casting-operator + + + include + #coalescing-operator + + + include + #custom-operator + + + + operator-declaration-keyword + + comment + operator-declaration + match + \b(operator|prefix|infix|postfix)\b + name + keyword.other.operator.swift + + optional-type + + beginCaptures + + 1 + + name + support.type.optional.swift + + 2 + + name + punctuation.definition.optional.begin.swift + + + end + (>) + endCaptures + + 1 + + name + punctuation.definition.optional.end.swift + + + match + \b(Optional)(<) + name + meta.optional.swift + patterns + + + include + $self + + + + overflow-operator + + match + (?<![/=\-+!*%<>&|\^~.])\&(\+|\-|\*|\/|%)(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.overflow.swift + + parameter-clause + + begin + (\() + beginCaptures + + 1 + + name + punctuation.definition.function-arguments.begin.swift + + + comment + parameter-clause + end + (\)) + endCaptures + + 1 + + name + punctuation.definition.function-arguments.end.swift + + + name + meta.parameter-clause.swift + patterns + + + captures + + 2 + + name + variable.parameter.function.swift + + + comment + named parameters + match + \b([\w^\d][\w\d]*)\b\s+\b([\w^\d][\w\d]*)\b + + + include + $self + + + + protocol-composition-type + + beginCaptures + + 1 + + name + support.type.protocol.swift + + 2 + + name + punctuation.definition.protocol.begin.swift + + + end + (>) + endCaptures + + 1 + + name + punctuation.definition.protocol.end.swift + + + match + \b(protocol)(<) + name + meta.protocol.swift + patterns + + + include + $self + + + + range-operator + + match + (?<![/=\-+!*%<>&|\^~.])\.\.[.<]?(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.range.swift + + remainder-operator + + match + (?<![/=\-+!*%<>&|\^~.])\%(?![/=\-+!*%<>&|\^~.]) + name + keyword.operator.remainder.swift + + section-punctuation + + patterns + + + captures + + 1 + + name + punctuation.section.scope.begin.swift + + 2 + + name + punctuation.section.scope.end.swift + + + comment + Allows the special return snippet to fire. + match + (\{)(\}) + + + captures + + 1 + + name + punctuation.section.scope.begin.swift + + 2 + + name + punctuation.section.scope.end.swift + + + comment + Allows the special return snippet to fire. + match + (\()(\)) + + + captures + + 1 + + name + punctuation.section.scope.begin.swift + + 2 + + name + punctuation.section.scope.end.swift + + + comment + Allows the special return snippet to fire. + match + (\[)(\]) + + + + shebang-line + + captures + + 1 + + name + punctuation.definition.comment.line.shebang.swift + + + comment + Shebang line + match + ^(#!).*$ + name + comment.line.shebang.swift + + special-literal + + match + \b__(FILE|LINE|COLUMN|FUNCTION)__\b + name + keyword.other.literal.swift + + storage-type + + match + \b(var|func|let|class|enum|struct|protocol|extension|typealias)\b + name + storage.type.swift + + string-literal + + begin + " + beginCaptures + + 0 + + name + punctuation.definition.string.begin.swift + + + end + " + endCaptures + + 0 + + name + punctuation.definition.string.end.swift + + + name + string.quoted.double.swift + patterns + + + match + \\[0\\tnr"'] + name + constant.character.escape.swift + + + match + \\u\{\h{1,8}\} + name + constant.character.escape.swift + + + begin + \\\( + beginCaptures + + 0 + + name + punctuation.section.embedded.begin.swift + + + contentName + source.swift + end + \) + endCaptures + + 0 + + name + punctuation.section.embedded.end.swift + + 1 + + name + source.swift + + + name + meta.embedded.line.swift + patterns + + + include + $self + + + begin + \( + comment + Nested parens + end + \) + + + + + match + \\. + name + invalid.illegal.unrecognized-escape.swift + + + + switch-statement-keyword + + comment + switch-statement + match + \b(switch|case|default|where)\b + name + keyword.control.switch.swift + + ternary-operator + + match + (?<=[\s(\[{,;:])(\?|:)(?=[\s)\]},;:]) + name + keyword.operator.ternary.swift + + type + + comment + type + patterns + + + include + #builtin-type + + + include + #collection-type + + + include + #optional-type + + + include + #protocol-composition-type + + + + type-casting-operator + + match + \b(is\b|as([!?]\B|\b)) + name + keyword.operator.type-casting.swift + + + scopeName + source.swift + uuid + D133338A-DEED-4ECC-9852-A392C44D10AC + + diff --git a/SyntaxKit/Tests/Resources/Fixtures/swifttest.swift.txt b/SyntaxKit/Tests/Resources/Fixtures/swifttest.swift.txt new file mode 100644 index 0000000..12e7413 --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/swifttest.swift.txt @@ -0,0 +1,75 @@ +// +// Pattern.swift +// SyntaxKit +// +// Created by Sam Soffes on 9/18/14. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// + +/** + * Test comment + */ + +import Foundation + +final class Pattern { + + // MARK: - Properties + + let name: String? + let match: String? + let captures: CaptureCollection? + let begin: String? + let beginCaptures: CaptureCollection? + let end: String? + let endCaptures: CaptureCollection? + private weak var parent: Pattern? + private let patterns: [Pattern] + + var superpattern: Pattern? { + return parent + } + + var subpatterns: [Pattern] { + return patterns + } + + // MARK: - Initializers + + init?(dictionary: [NSObject: AnyObject], parent: Pattern? = nil) { + self.parent = parent + let i = 0.9 + self.name = dictionary["name"] as? String + self.match = dictionary["match"] as? String + self.begin = dictionary["begin"] as? String + self.end = dictionary["end"] as? String + + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { + self.beginCaptures = CaptureCollection(dictionary: dictionary) + } else { + self.beginCaptures = nil + } + + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { + self.captures = CaptureCollection(dictionary: dictionary) + } else { + self.captures = nil + } + + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { + self.endCaptures = CaptureCollection(dictionary: dictionary) + } else { + self.endCaptures = nil + } + + var patterns = [Pattern]() + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + for value in array { + if let pattern = Pattern(dictionary: value, parent: parent) { + patterns.append(pattern) + } + } + } + self.patterns = patterns + } +} \ No newline at end of file diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift new file mode 100644 index 0000000..06737f5 --- /dev/null +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -0,0 +1,61 @@ +// +// SwiftBaselineHighlightingTests.swift +// SyntaxKit +// +// Created by Alexander Hedges on 19/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import UIKit +import XCTest +import SyntaxKit + +class SwiftBaselineHighlightingTests: XCTestCase { + + let parser = AttributedParser(language: language("Swift"), theme: theme("Solarized")) + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testColors() { + let input = fixture("swifttest.swift", "txt") + let string = parser.attributedStringForString(input) + + // line comment + assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(10, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(135, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + + // block comment +// print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) + assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + + // string literal + print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) + assertEqualColors(Color(hex: "#839496")!, string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + var stringRange = NSRange() + assertEqualColors(Color(hex: "#2aa198")!, string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as! Color) + XCTAssertEqual(stringRange.length, 4) + assertEqualColors(Color(hex: "#839496")!, string.attributesAtIndex(749, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + + // number literal + var numberRange = NSRange() +// print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) + let attribute = string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as! Color + XCTAssertNotNil(attribute) + assertEqualColors(Color(hex: "#d33682")!, attribute) + XCTAssertEqual(numberRange, NSRange(location: 715, length: 3)) + } + + func testHighlightingPerformance() { + let input = fixture("swifttest.swift", "txt") + self.measureBlock { + self.parser.attributedStringForString(input) + } + } + +} diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 1c00a65..79692c7 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -14,7 +14,7 @@ class ThemeTests: XCTestCase { // MARK: - Properties let tomorrow = theme("Tomorrow") - + let solarized = theme("Solarized") // MARK: - Tests @@ -22,5 +22,13 @@ class ThemeTests: XCTestCase { XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) XCTAssertEqual("Tomorrow", tomorrow.name) assertEqualColors(Color(hex: "#666969")!, tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#4271AE")!, tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as! Color) } + + func testComplexTheme() { + XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) + XCTAssertEqual("Solarized (light)", solarized.name) + assertEqualColors(Color(hex: "#2aa198")!, solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#2aa198")!, solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as! Color) + } } From 09369bd5e48d81504b4146194b2a6295abd9c803 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 22 Jan 2016 23:48:09 +0100 Subject: [PATCH 009/110] get the parser working on the swift test file --- ...36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 22 ++ .../Info.plist | 40 ++++ SyntaxKit/Parser.swift | 215 +++++++++--------- SyntaxKit/Pattern.swift | 18 +- SyntaxKit/Repository.swift | 5 + SyntaxKit/ResultSet.swift | 22 +- .../SwiftBaselineHighlightingTests.swift | 17 +- SyntaxKit/Tests/TestHelper.swift | 14 +- SyntaxKit/Tests/ThemeTests.swift | 8 +- 9 files changed, 210 insertions(+), 151 deletions(-) create mode 100644 SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist create mode 100644 SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist new file mode 100644 index 0000000..6962515 --- /dev/null +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -0,0 +1,22 @@ + + + + + classNames + + SwiftBaselineHighlightingTests + + testHighlightingPerformance() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.162 + baselineIntegrationDisplayName + 22 Jan 2016 23:43:44 + + + + + + diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist new file mode 100644 index 0000000..ee635f9 --- /dev/null +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist @@ -0,0 +1,40 @@ + + + + + runDestinationsByUUID + + 36C0E679-EC89-495E-854F-FFBDEA1A4F58 + + localComputer + + busSpeedInMHz + 100 + cpuCount + 1 + cpuKind + 6-Core Intel Xeon E5 + cpuSpeedInMHz + 3500 + logicalCPUCoresPerPackage + 12 + modelCode + MacPro6,1 + physicalCPUCoresPerPackage + 6 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + i386 + targetDevice + + modelCode + iPad2,1 + platformIdentifier + com.apple.platform.iphonesimulator + + + + + diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index fd656ab..7ae4c0c 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -2,6 +2,11 @@ // Parser.swift // SyntaxKit // +// This class is in charge of the painful task of recognizing the syntax patterns +// Tries to match the output of TextMate as closely as possible. +// Turns out TextMate doesn't highlight things it should highlight according to the +// grammar so this is not entirely straight forward. +// // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -30,129 +35,132 @@ public class Parser { // MARK: - Parsing public func parse(string: String, match callback: Callback) { - // Loop through paragraphs - let s: NSString = string - let length = s.length - var paragraphEnd = 0 - - while paragraphEnd < length { - let paragraphStart = paragraphEnd - var newParagraphStart = 0 - s.getParagraphStart(&newParagraphStart, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) - if newParagraphStart < paragraphStart - 1 { - newParagraphStart = paragraphStart - } - - let paragraphRange = NSMakeRange(newParagraphStart, paragraphEnd - newParagraphStart) - let limit = NSMaxRange(paragraphRange) - var range = paragraphRange - - // Loop through the line until we reach the end - while range.length > 0 && range.location < limit { -// print(range) -// print(s.substringWithRange(range)) -// if s.substringWithRange(range).containsString("class A < B") { -// print("There") -// } - let matches = matchPatterns(language.patterns, withString: string, inRange: range) - var newLocation = limit - if matches.results.count != 0 { - let newPlace = Int(applyResults(matches, callback: callback)) - if newPlace != range.location { - newLocation = newPlace - } - } - if newLocation > limit { - paragraphEnd = newLocation - break - } - assert(range.length - (newLocation - range.location) >= 0) - range.length = range.length - (newLocation - range.location) - range.location = newLocation - } - } + let results = self.matchPatterns(language.patterns, withString: string, withEndPatternFromPattern: nil, startingAtIndex: 0) + self.applyResults(results, callback: callback) } - - - // MARK: - Private - private func matchPatterns(patterns: Patterns, withString string: String, inRange bounds: NSRange) -> ResultSet { - var bestResult = ResultSet() - for pattern in patterns.getContent() { -// print("Pattern: \(pattern.name)") - if let match = pattern.match { - if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { - if !resultSet.isEmpty && resultSet.range!.length != 0 && bestResult.hasLowerPriorityThan(resultSet) { - bestResult = resultSet - } - } - } else if let begin = pattern.begin, end = pattern.end { - guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { continue } - - let beginRange = beginResults.range! - var newLocation = NSMaxRange(beginRange) - - let s: NSString = string - assert(s.length - newLocation >= 0) - var endBounds = NSRange(location: newLocation, length: s.length - newLocation) - - var midResults = ResultSet() + // MARK: - Private + + /// + /// + private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int) -> ResultSet? { + assert(endPattern == nil || endPattern!.end != nil) + + let s: NSString = string + let length = s.length + var paragraphStart = startIndex + var paragraphEnd = startIndex + var result: ResultSet? + + while paragraphEnd < length { + s.getLineStart(nil, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) + var range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) + + while range.length > 0 { + let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) - if pattern.subpatterns.getContent().count >= 1 { - let midTestResults = matchPatterns(pattern.subpatterns, withString: string, inRange: endBounds) - let midRange = midTestResults.range - if midRange != nil { - midResults.addResults(midTestResults) - newLocation = NSMaxRange(midRange!) - assert(endBounds.length - (newLocation - endBounds.location) >= 0) - endBounds = NSRange(location: newLocation, length: endBounds.length - (newLocation - endBounds.location)) + if bestResultForMiddle != nil && !bestResultForMiddle!.isEmpty && bestResultForMiddle!.range.length != 0 { + if result == nil { + result = bestResultForMiddle + } else { + result!.addResults(bestResultForMiddle!) } + let newStart = NSMaxRange(bestResultForMiddle!.range) +// if newStart > paragraphEnd { +// s.getParagraphStart(nil, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) +// range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) +// paragraphStart = newStart +// } else { + range = NSRange(location: newStart, length: range.length - (newStart - range.location)) + paragraphEnd = newStart +// } } - let endResults = matchExpression(end, withString: string, inRange: endBounds, captures: pattern.endCaptures) - if endResults == nil { - continue - } - let endRange = endResults!.range! - - var results = ResultSet() - if let name = pattern.name { - results.addResult(Result(scope: name, range: NSUnionRange(beginRange, endRange))) + if endPattern != nil { + var endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) + if result != nil { + endMatchResult?.addResults(result!) + } + if endMatchResult != nil { + return endMatchResult + } } - results.addResults(beginResults) - results.addResults(midResults) - results.addResults(endResults!) - if bestResult.hasLowerPriorityThan(results) { - bestResult = results + if bestResultForMiddle == nil || bestResultForMiddle!.isEmpty || bestResultForMiddle!.range.length == 0 { + range = NSRange(location: paragraphEnd, length: 0) } - } else if pattern.subpatterns.getContent().count >= 1 { - var subPatternTry = matchPatterns(pattern.subpatterns, withString: string, inRange: bounds) - if bestResult.hasLowerPriorityThan(subPatternTry) { - if pattern.name != nil && subPatternTry.range != nil { - subPatternTry.addResult(Result(scope: pattern.name!, range: subPatternTry.range!)) - } - bestResult = subPatternTry + } + paragraphStart = paragraphEnd++ + } + + if endPattern != nil { // failed to match end pattern + return nil + } + return result + } + + /// + private func findBestPatternInPatterns(patterns: Patterns, inString string: String, inRange range: NSRange) -> ResultSet? { + var bestResultForMiddle: ResultSet? + for pattern in patterns.getContent() { + let currRes = self.matchPattern(pattern, inString: string, inRange: range) + if bestResultForMiddle == nil || bestResultForMiddle!.hasLowerPriorityThan(currRes) { + bestResultForMiddle = currRes + } + } + return bestResultForMiddle + } + + /// + private func matchPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { + if let match = pattern.match { + if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + if resultSet.range.length != 0 { + return resultSet } } + } else if let begin = pattern.begin, _ = pattern.end { + guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { + return nil + } + + let newLocation = NSMaxRange(beginResults.range) + guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation) else { + return nil + } + var result = ResultSet(startingRange: endResults.range) + if pattern.name != nil { + result.addResult(Result(scope: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) + } + result.addResults(beginResults) + result.addResults(endResults) + return result + } else if pattern.subpatterns.getContent().count >= 1 { + var result = findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) + if pattern.name != nil { + result?.addResult(Result(scope: pattern.name!, range: result!.range)) + } + return result } - return bestResult + return nil } /// Matches a given regular expression in a String /// /// - /// - returns: The resultset + /// - returns: The set containing the results. May be nil if the expression could not match any part of the string. It may also be empty and only contain private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { - let matches = regularExpression.matchesInString(string, options: [], range: bounds) + let matches = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds) if matches.first == nil || matches.first!.range.location == NSNotFound { return nil } let result = matches.first! - var resultSet = ResultSet() - resultSet.addResult(Result(scope: baseSelector ?? "", range: result.range)) + var resultSet = ResultSet(startingRange: result.range) + if baseSelector != nil { + resultSet.addResult(Result(scope: baseSelector!, range: result.range)) + } if let captures = captures { for index in captures.captureIndexes { @@ -166,18 +174,15 @@ public class Parser { } } } - + return resultSet } - private func applyResults(resultSet: ResultSet, callback: Callback) -> UInt { - var i = 0 - for result in resultSet.results { + private func applyResults(resultSet: ResultSet?, callback: Callback) { + for result in resultSet?.results ?? [] { if result.scope != "" && result.range.length > 0 { callback(scope: result.scope, range: result.range) } - i = max(NSMaxRange(result.range), i) } - return UInt(i) } } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 82ba11b..c26d342 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -37,19 +37,19 @@ final class Pattern { self.name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] + self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) //[.CaseInsensitive] } else { self.match = nil } if let beginExpr = dictionary["begin"] as? String { - self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) } else { self.begin = nil } if let endExpr = dictionary["end"] as? String { - self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) } else { self.end = nil } @@ -79,16 +79,4 @@ final class Pattern { self.patterns = Patterns(array: [], repository: repository) } } - - init(pattern: Pattern, parent: Pattern? = nil) { - self.name = pattern.name - self.match = pattern.match - self.captures = pattern.captures - self.begin = pattern.begin - self.beginCaptures = pattern.beginCaptures - self.end = pattern.end - self.endCaptures = pattern.endCaptures - self.parent = parent - self.patterns = pattern.patterns - } } diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 99cab56..2ced937 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -23,6 +23,11 @@ class Repository { self.entries[key] = pattern } } + for (key, value) in repo { // now that we have all dependencies, try again + if let pattern = Pattern(dictionary: value, repository: self) { + self.entries[key] = pattern + } + } } func allEntries() -> [String: Pattern] { diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 20b8d18..6a0d9b2 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -17,23 +17,25 @@ struct ResultSet { return _results } - var range: NSRange? + var range: NSRange var isEmpty: Bool { return results.isEmpty } + init(startingRange range: NSRange) { + self.range = range + } + // MARK: - Comparing func hasLowerPriorityThan(other: ResultSet?) -> Bool { - if other == nil || other!.range == nil { + if other == nil { return false - } else if self.range == nil { - return true - } else if self.range!.location != other!.range!.location { - return self.range!.location > other!.range!.location + } else if other!.range.location != self.range.location { + return other!.range.location < self.range.location } else { - return self.range!.length < other!.range!.length + return other!.range.length > self.range.length } } @@ -42,15 +44,11 @@ struct ResultSet { mutating func addResult(result: Result) { _results.append(result) - guard let range = range else { - self.range = result.range - return - } - self.range = NSUnionRange(range, result.range) } mutating func addResults(resultSet: ResultSet) { + self.range = NSUnionRange(range, resultSet.range) for result in resultSet.results { addResult(result) } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 06737f5..0daca6e 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -27,27 +27,25 @@ class SwiftBaselineHighlightingTests: XCTestCase { let string = parser.attributedStringForString(input) // line comment - assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(10, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) - assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(135, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // block comment // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) - assertEqualColors(Color(hex: "#93A1A1")!, string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // string literal print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) - assertEqualColors(Color(hex: "#839496")!, string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) var stringRange = NSRange() - assertEqualColors(Color(hex: "#2aa198")!, string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#2aa198"), string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(stringRange.length, 4) - assertEqualColors(Color(hex: "#839496")!, string.attributesAtIndex(749, effectiveRange: nil)[NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // number literal var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) - let attribute = string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as! Color - XCTAssertNotNil(attribute) - assertEqualColors(Color(hex: "#d33682")!, attribute) + assertEqualColors(Color(hex: "#d33682"), string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 3)) } @@ -57,5 +55,4 @@ class SwiftBaselineHighlightingTests: XCTestCase { self.parser.attributedStringForString(input) } } - } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index a1f5dde..7cb2f22 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -83,11 +83,15 @@ extension Color { } #endif -func assertEqualColors(color1: Color, _ color2: Color, accuracy: CGFloat = 0.005) { - XCTAssertEqualWithAccuracy(color1.redComponent, color2.redComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1.greenComponent, color2.greenComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1.blueComponent, color2.blueComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1.alphaComponent, color2.alphaComponent, accuracy: accuracy) +func assertEqualColors(color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.005) { + if color1 == nil || color2 == nil { + XCTAssert(false, "colors have to be non-nil") + return + } + XCTAssertEqualWithAccuracy(color1!.redComponent, color2!.redComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.greenComponent, color2!.greenComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.blueComponent, color2!.blueComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.alphaComponent, color2!.alphaComponent, accuracy: accuracy) } extension NSRange: Equatable { } diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 79692c7..a539b9a 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -21,14 +21,14 @@ class ThemeTests: XCTestCase { func testLoading() { XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969")!, tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as! Color) - assertEqualColors(Color(hex: "#4271AE")!, tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) } func testComplexTheme() { XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) XCTAssertEqual("Solarized (light)", solarized.name) - assertEqualColors(Color(hex: "#2aa198")!, solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as! Color) - assertEqualColors(Color(hex: "#2aa198")!, solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as! Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as? Color) } } From f32ce8c72f0d773e2be859fbe641784f5d728885 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 22 Jan 2016 23:51:02 +0100 Subject: [PATCH 010/110] add extra sanitation check --- SyntaxKit/Parser.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 7ae4c0c..3989ef0 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -71,7 +71,7 @@ public class Parser { // range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) // paragraphStart = newStart // } else { - range = NSRange(location: newStart, length: range.length - (newStart - range.location)) + range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) paragraphEnd = newStart // } } From 6d624997714d3de4d913b50a0bd6517ceafc7300 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 23 Jan 2016 18:00:45 +0100 Subject: [PATCH 011/110] readjust tests and have the parsing order on a first come first serve basis --- .../36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 4 ++-- SyntaxKit/Pattern.swift | 6 +++--- SyntaxKit/ResultSet.swift | 4 +--- SyntaxKit/Tests/ParserTests.swift | 9 +++++---- SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist index 6962515..b813d33 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -11,9 +11,9 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.162 + 0.108 baselineIntegrationDisplayName - 22 Jan 2016 23:43:44 + Local Baseline diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index c26d342..3ee6ce0 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -37,19 +37,19 @@ final class Pattern { self.name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) //[.CaseInsensitive] + self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] } else { self.match = nil } if let beginExpr = dictionary["begin"] as? String { - self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) + self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) } else { self.begin = nil } if let endExpr = dictionary["end"] as? String { - self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines/*, .AllowCommentsAndWhitespace*/]) + self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { self.end = nil } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 6a0d9b2..64d0f0e 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -32,10 +32,8 @@ struct ResultSet { func hasLowerPriorityThan(other: ResultSet?) -> Bool { if other == nil { return false - } else if other!.range.location != self.range.location { - return other!.range.location < self.range.location } else { - return other!.range.length > self.range.length + return other!.range.location < self.range.location } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 4456b8c..9191e86 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -37,9 +37,9 @@ class ParserTests: XCTestCase { } } - XCTAssertEqual(NSMakeRange(7, 13), stringQuoted!) - XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin!) - XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd!) + XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) + XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin) + XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) } func testParsingBeginEndCrap() { @@ -51,10 +51,11 @@ class ParserTests: XCTestCase { } } - XCTAssertEqual(NSMakeRange(39, 4), stringQuoted!) + XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) } func testParsingGarbage() { + parser.parse("") { _, _ in } parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 0daca6e..269f504 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -46,7 +46,7 @@ class SwiftBaselineHighlightingTests: XCTestCase { var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) assertEqualColors(Color(hex: "#d33682"), string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) - XCTAssertEqual(numberRange, NSRange(location: 715, length: 3)) + XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } func testHighlightingPerformance() { From 3eb0d2ffdd35b26dc15af66057f67813b3a58a85 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 23 Jan 2016 23:18:57 +0100 Subject: [PATCH 012/110] more fixes, including to grammars Some of the grammar regexes are not valid in CocoaLand. --- SyntaxKit/Parser.swift | 10 ++-------- SyntaxKit/Pattern.swift | 19 +++++++++++++------ SyntaxKit/ResultSet.swift | 2 +- .../Tests/Resources/Fixtures/Ruby.tmLanguage | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 3989ef0..fb5399a 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -66,14 +66,8 @@ public class Parser { result!.addResults(bestResultForMiddle!) } let newStart = NSMaxRange(bestResultForMiddle!.range) -// if newStart > paragraphEnd { -// s.getParagraphStart(nil, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) -// range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) -// paragraphStart = newStart -// } else { - range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) - paragraphEnd = newStart -// } + range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) + paragraphEnd = newStart } if endPattern != nil { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 3ee6ce0..1fd7389 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -33,9 +33,9 @@ final class Pattern { // MARK: - Initializers init?(dictionary: [NSObject: AnyObject], repository: Repository, parent: Pattern? = nil) { - self.parent = parent - self.name = dictionary["name"] as? String - + self.parent = parent + self.name = dictionary["name"] as? String + if let matchExpr = dictionary["match"] as? String { self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] } else { @@ -47,15 +47,14 @@ final class Pattern { } else { self.begin = nil } - + if let endExpr = dictionary["end"] as? String { self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { self.end = nil } - - if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { self.beginCaptures = CaptureCollection(dictionary: dictionary) } else { self.beginCaptures = nil @@ -78,5 +77,13 @@ final class Pattern { } else { self.patterns = Patterns(array: [], repository: repository) } + + if dictionary["match"] as? String != nil && self.match == nil { + return nil + } + + if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + return nil + } } } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 64d0f0e..9bdb521 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -33,7 +33,7 @@ struct ResultSet { if other == nil { return false } else { - return other!.range.location < self.range.location + return self.range.location > other!.range.location } } diff --git a/SyntaxKit/Tests/Resources/Fixtures/Ruby.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Ruby.tmLanguage index ece00af..5a53e74 100644 --- a/SyntaxKit/Tests/Resources/Fixtures/Ruby.tmLanguage +++ b/SyntaxKit/Tests/Resources/Fixtures/Ruby.tmLanguage @@ -455,7 +455,7 @@ (?>[a-zA-Z_]\w*(?>[?!]|=(?!>))? # the method name |===?|>[>=]?|<=>|<[<=]?|[%&`/\|]|\*\*?|=?~|[-+]@?|\[\]=?) ) # …or an operator method [ \t] # the space separating the arguments - (?=[ \t]*[^\s#;]) # make sure arguments and not a comment follow + (?=[ \t]*[^\s\#;]) # make sure arguments and not a comment follow beginCaptures From 6f747706d61b7b6748aaa39924bdf6329e4be093 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 24 Jan 2016 01:02:02 +0100 Subject: [PATCH 013/110] minor adjustments --- .../xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme | 9 --------- SyntaxKit/AttributedParser.swift | 2 ++ SyntaxKit/Parser.swift | 5 +---- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index 188cf4a..0b08ff7 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -79,15 +79,6 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES"> - - - - diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 9248fd6..a5a0ff2 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -36,11 +36,13 @@ public class AttributedParser: Parser { public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { let output = NSMutableAttributedString(string: string, attributes: baseAttributes) + output.beginEditing() parse(string) { _, range, attributes in if let attributes = attributes { output.addAttributes(attributes, range: range) } } + output.endEditing() return output } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index fb5399a..d9b1b20 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -144,13 +144,10 @@ public class Parser { /// /// - returns: The set containing the results. May be nil if the expression could not match any part of the string. It may also be empty and only contain private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { - let matches = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds) - if matches.first == nil || matches.first!.range.location == NSNotFound { + guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { return nil } - let result = matches.first! - var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(scope: baseSelector!, range: result.range)) From 48a9416045523bd5dd4fa3e01142487ebcb340a8 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 24 Jan 2016 22:03:14 +0100 Subject: [PATCH 014/110] Add documentation to parser and inline result priority comparisons --- SyntaxKit/Parser.swift | 87 ++++++++++++++++++++++++++++++++++----- SyntaxKit/ResultSet.swift | 10 ----- 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index d9b1b20..5dfb509 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -2,12 +2,12 @@ // Parser.swift // SyntaxKit // -// This class is in charge of the painful task of recognizing the syntax patterns -// Tries to match the output of TextMate as closely as possible. -// Turns out TextMate doesn't highlight things it should highlight according to the -// grammar so this is not entirely straight forward. +// This class is in charge of the painful task of recognizing the syntax +// patterns. It tries to match the output of TextMate as closely as possible. +// Turns out TextMate doesn't highlight things it should highlight according to +// the grammar so this is not entirely straight forward. // -// Created by Sam Soffes on 9/19/14. +// Created by Sam Soffes on 9/19/14. Edited by Alexander Hedges // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -23,7 +23,8 @@ public class Parser { // MARK: - Properties public let language: Language - + +// private var scopesString: NSMutableAttributedString? // MARK: - Initializers @@ -33,6 +34,10 @@ public class Parser { // MARK: - Parsing + +// public func rangeToParseInString(string: String, changeAtLocation location: Int) -> NSRange { +// return NSRange(location: 0, length: (string as NSString).length) +// } public func parse(string: String, match callback: Callback) { let results = self.matchPatterns(language.patterns, withString: string, withEndPatternFromPattern: nil, startingAtIndex: 0) @@ -41,8 +46,30 @@ public class Parser { // MARK: - Private + // Algorithmic notes: + // A pattern expression can not match a substring spanning multiple lines + // so in the outer loop the string is decomposed into its lines. + // In the inner loop it tries to repeatedly match a pattern followed by the + // end pattern until either the line is consumed or it has found the end. + // This procedure is repeated with the subsequent lines until it has either + // matched the end pattern or the string is consumed entirely. + // If it can find neither in a line it moves to the next one. + + // Implementation note: + // The matching of the middle part may return a match that goes beyond the + // given range. this is intentional + + /// Matches an array of patterns in the input + /// + /// - parameter patterns: The patterns to match the sting against + /// - parameter string: The matched string + /// - parameter endPattern: If specified, the pattern at which to stop + /// the matching process. Otherwise it will just + /// match the entire string. + /// - parameter startingIndex: The index at which to start matching /// - /// + /// - returns: The result set containing the lexical scope names with range + /// information or nil of nothing could be found. private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int) -> ResultSet? { assert(endPattern == nil || endPattern!.end != nil) @@ -93,19 +120,49 @@ public class Parser { return result } + /// Helper method that iterates over the given patterns and tries to match + /// them in order. + /// + /// It returns the best match, if many are possible. Which is the result + /// that starts the soonest and is encountered first. + /// + /// - parameter patterns: The patterns that should be matched + /// - parameter string: The string that should be matched against + /// - parameter range: The range in which the matching should happen. + /// Though it is not guaranteed that the length of + /// the result does not exceed the length of the + /// range. /// + /// - returns: The results. nil if nothing could be matched and an empty + /// set if something could be matched but it doesn't have any + /// information associated with the match. private func findBestPatternInPatterns(patterns: Patterns, inString string: String, inRange range: NSRange) -> ResultSet? { var bestResultForMiddle: ResultSet? for pattern in patterns.getContent() { let currRes = self.matchPattern(pattern, inString: string, inRange: range) - if bestResultForMiddle == nil || bestResultForMiddle!.hasLowerPriorityThan(currRes) { + if currRes?.range.location == range.location { + return currRes + } else if bestResultForMiddle == nil || currRes != nil && currRes!.range.location < bestResultForMiddle!.range.location { bestResultForMiddle = currRes } } return bestResultForMiddle } + // Implementation note: + // The order in which the beginning middle and end are added to the final + // result matters. + + /// Matches a single pattern in the string in the given range + /// + /// A pattern may be one three options: + /// * A single pattern called match which should be matched + /// * A begin and an end pattern containing an optional body of patterns + /// which should be matched between the begin and the end + /// * Only a body of patterns without the begin and end. Any pattern may be + /// matched successfully /// + /// - returns: The result of the match. Nil if unsuccessful private func matchPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { if let match = pattern.match { if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { @@ -139,10 +196,20 @@ public class Parser { return nil } - /// Matches a given regular expression in a String + /// Matches a given regular expression in a String and returns range + /// information for the captures /// + /// - parameter expression: The regular expression to match + /// - parameter string: The string to match against + /// - parameter range: The range to which to restrict the match + /// - parameter captures: A collection of captures that can be used to add + /// extra information to parts of the match. + /// - parameter baseSelector: String to associate with the entire range of + /// the match /// - /// - returns: The set containing the results. May be nil if the expression could not match any part of the string. It may also be empty and only contain + /// - returns: The set containing the results. May be nil if the expression + /// could not match any part of the string. It may also be empty + /// and only contain range information to show what it matched. private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { return nil diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 9bdb521..3d29d49 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -26,16 +26,6 @@ struct ResultSet { init(startingRange range: NSRange) { self.range = range } - - // MARK: - Comparing - - func hasLowerPriorityThan(other: ResultSet?) -> Bool { - if other == nil { - return false - } else { - return self.range.location > other!.range.location - } - } // MARK: - Adding From 6f90bc150c789ac17dcaec30ba1eadf66d5ca3ae Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 26 Jan 2016 19:15:57 +0100 Subject: [PATCH 015/110] Reformat hard tabs to soft tabs Soft tabs is the way to go (and the default in xcode, afaik). --- SyntaxKit/AttributedParser.swift | 102 ++++++++--------- SyntaxKit/Capture.swift | 16 +-- SyntaxKit/CaptureCollection.swift | 44 ++++---- SyntaxKit/Color.swift | 16 +-- SyntaxKit/Include.swift | 4 +- SyntaxKit/Language.swift | 34 +++--- SyntaxKit/Parser.swift | 118 ++++++++++---------- SyntaxKit/Pattern.swift | 66 +++++------ SyntaxKit/Patterns.swift | 8 +- SyntaxKit/Repository.swift | 8 +- SyntaxKit/Result.swift | 16 +-- SyntaxKit/ResultSet.swift | 40 +++---- SyntaxKit/Tests/AttributedParserTests.swift | 20 ++-- SyntaxKit/Tests/LanguageTests.swift | 28 ++--- SyntaxKit/Tests/ParserTests.swift | 96 ++++++++-------- SyntaxKit/Tests/TestHelper.swift | 114 +++++++++---------- SyntaxKit/Tests/ThemeTests.swift | 18 +-- SyntaxKit/Theme.swift | 62 +++++----- 18 files changed, 405 insertions(+), 405 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index a5a0ff2..2afeed1 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -8,68 +8,68 @@ public class AttributedParser: Parser { - // MARK: - Types + // MARK: - Types - public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void + public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void - // MARK: - Properties + // MARK: - Properties - public let theme: Theme + public let theme: Theme - // MARK: - Initializers + // MARK: - Initializers - public required init(language: Language, theme: Theme) { - self.theme = theme - super.init(language: language) - } + public required init(language: Language, theme: Theme) { + self.theme = theme + super.init(language: language) + } - // MARK: - Parsing + // MARK: - Parsing - public func parse(string: String, match callback: AttributedCallback) { - parse(string) { scope, range in - callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) - } - } + public func parse(string: String, match callback: AttributedCallback) { + parse(string) { scope, range in + callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) + } + } - public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { - let output = NSMutableAttributedString(string: string, attributes: baseAttributes) + public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { + let output = NSMutableAttributedString(string: string, attributes: baseAttributes) output.beginEditing() - parse(string) { _, range, attributes in - if let attributes = attributes { - output.addAttributes(attributes, range: range) - } - } + parse(string) { _, range, attributes in + if let attributes = attributes { + output.addAttributes(attributes, range: range) + } + } output.endEditing() - return output - } - - - // MARK: - Private - - private func attributesForScope(scope: String) -> Attributes? { - let components = scope.componentsSeparatedByString(".") as NSArray - let count = components.count - if count == 0 { - return nil - } - - var attributes = Attributes() - for i in 0.. Attributes? { + let components = scope.componentsSeparatedByString(".") as NSArray + let count = components.count + if count == 0 { + return nil + } + + var attributes = Attributes() + for i in 0.. Capture? { - return captures[index] - } + subscript(index: UInt) -> Capture? { + return captures[index] + } } diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index 37ada15..9de28ec 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -9,7 +9,7 @@ #if os(OSX) import AppKit.NSColor public typealias ColorType = NSColor - + extension NSColor { public convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { self.init(SRGBRed: red, green: green, blue: blue, alpha: alpha) @@ -25,20 +25,20 @@ public typealias Color = ColorType extension Color { public convenience init?(hex s: String) { var hex: NSString = s - + // Remove `#` and `0x` if hex.hasPrefix("#") { hex = hex.substringFromIndex(1) } else if hex.hasPrefix("0x") { hex = hex.substringFromIndex(2) } - + // Invalid if not 3, 6, or 8 characters let length = hex.length if length != 3 && length != 6 && length != 8 { return nil } - + // Make the string 8 characters long for easier parsing if length == 3 { let r = hex.substringWithRange(NSMakeRange(0, 1)) @@ -48,18 +48,18 @@ extension Color { } else if length == 6 { hex = String(hex) + "ff" } - + // Convert 2 character strings to CGFloats func hexValue(string: String) -> CGFloat { let value = Double(strtoul(string, nil, 16)) return CGFloat(value / 255.0) } - + let red = hexValue(hex.substringWithRange(NSMakeRange(0, 2))) let green = hexValue(hex.substringWithRange(NSMakeRange(2, 2))) let blue = hexValue(hex.substringWithRange(NSMakeRange(4, 2))) let alpha = hexValue(hex.substringWithRange(NSMakeRange(6, 2))) - + self.init(red: red, green: green, blue: blue, alpha: alpha) } -} \ No newline at end of file +} diff --git a/SyntaxKit/Include.swift b/SyntaxKit/Include.swift index 3958eec..886343b 100644 --- a/SyntaxKit/Include.swift +++ b/SyntaxKit/Include.swift @@ -11,5 +11,5 @@ import Foundation class Include { - -} \ No newline at end of file + +} diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index a1a935d..2445a0a 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -10,25 +10,25 @@ import Foundation public struct Language { - // MARK: - Properties + // MARK: - Properties - public let UUID: String - public let name: String - public let scopeName: String - let patterns: Patterns + public let UUID: String + public let name: String + public let scopeName: String + let patterns: Patterns - // MARK: - Initializers + // MARK: - Initializers - public init?(dictionary: [NSObject: AnyObject]) { - guard let UUID = dictionary["uuid"] as? String, - name = dictionary["name"] as? String, - scopeName = dictionary["scopeName"] as? String - else { return nil } + public init?(dictionary: [NSObject: AnyObject]) { + guard let UUID = dictionary["uuid"] as? String, + name = dictionary["name"] as? String, + scopeName = dictionary["scopeName"] as? String + else { return nil } - self.UUID = UUID - self.name = name - self.scopeName = scopeName + self.UUID = UUID + self.name = name + self.scopeName = scopeName let repository: Repository if let repo = dictionary["repository"] as? [String: [NSObject: AnyObject]] { @@ -36,11 +36,11 @@ public struct Language { } else { repository = Repository(repo: [:]) } - + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - self.patterns = Patterns(array: array, repository: repository) + self.patterns = Patterns(array: array, repository: repository) } else { self.patterns = Patterns(array: [], repository: repository) } - } + } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 5dfb509..d16dea0 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -2,7 +2,7 @@ // Parser.swift // SyntaxKit // -// This class is in charge of the painful task of recognizing the syntax +// This class is in charge of the painful task of recognizing the syntax // patterns. It tries to match the output of TextMate as closely as possible. // Turns out TextMate doesn't highlight things it should highlight according to // the grammar so this is not entirely straight forward. @@ -15,37 +15,37 @@ import Foundation public class Parser { - // MARK: - Types + // MARK: - Types - public typealias Callback = (scope: String, range: NSRange) -> Void + public typealias Callback = (scope: String, range: NSRange) -> Void - // MARK: - Properties + // MARK: - Properties + + public let language: Language - public let language: Language - // private var scopesString: NSMutableAttributedString? - // MARK: - Initializers + // MARK: - Initializers + + public init(language: Language) { + self.language = language + } - public init(language: Language) { - self.language = language - } + // MARK: - Parsing - // MARK: - Parsing - -// public func rangeToParseInString(string: String, changeAtLocation location: Int) -> NSRange { -// return NSRange(location: 0, length: (string as NSString).length) +// public func scopeForChangeInString(newString: String, atLocation location: Int) -> NSRange { +// return NSRange(location: 0, length: (newString as NSString).length) // } - public func parse(string: String, match callback: Callback) { + public func parse(string: String, inScope scope: NSRange? = nil, match callback: Callback) { let results = self.matchPatterns(language.patterns, withString: string, withEndPatternFromPattern: nil, startingAtIndex: 0) self.applyResults(results, callback: callback) - } - + } + // MARK: - Private - + // Algorithmic notes: // A pattern expression can not match a substring spanning multiple lines // so in the outer loop the string is decomposed into its lines. @@ -54,13 +54,13 @@ public class Parser { // This procedure is repeated with the subsequent lines until it has either // matched the end pattern or the string is consumed entirely. // If it can find neither in a line it moves to the next one. - + // Implementation note: // The matching of the middle part may return a match that goes beyond the - // given range. this is intentional - + // given range. This is intentional. + /// Matches an array of patterns in the input - /// + /// /// - parameter patterns: The patterns to match the sting against /// - parameter string: The matched string /// - parameter endPattern: If specified, the pattern at which to stop @@ -72,20 +72,20 @@ public class Parser { /// information or nil of nothing could be found. private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int) -> ResultSet? { assert(endPattern == nil || endPattern!.end != nil) - + let s: NSString = string let length = s.length var paragraphStart = startIndex var paragraphEnd = startIndex var result: ResultSet? - + while paragraphEnd < length { s.getLineStart(nil, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) var range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) while range.length > 0 { let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) - + if bestResultForMiddle != nil && !bestResultForMiddle!.isEmpty && bestResultForMiddle!.range.length != 0 { if result == nil { result = bestResultForMiddle @@ -96,7 +96,7 @@ public class Parser { range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) paragraphEnd = newStart } - + if endPattern != nil { var endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) if result != nil { @@ -106,20 +106,20 @@ public class Parser { return endMatchResult } } - + if bestResultForMiddle == nil || bestResultForMiddle!.isEmpty || bestResultForMiddle!.range.length == 0 { range = NSRange(location: paragraphEnd, length: 0) } } paragraphStart = paragraphEnd++ } - + if endPattern != nil { // failed to match end pattern return nil } return result } - + /// Helper method that iterates over the given patterns and tries to match /// them in order. /// @@ -129,8 +129,8 @@ public class Parser { /// - parameter patterns: The patterns that should be matched /// - parameter string: The string that should be matched against /// - parameter range: The range in which the matching should happen. - /// Though it is not guaranteed that the length of - /// the result does not exceed the length of the + /// Though it is not guaranteed that the length of + /// the result does not exceed the length of the /// range. /// /// - returns: The results. nil if nothing could be matched and an empty @@ -148,18 +148,18 @@ public class Parser { } return bestResultForMiddle } - + // Implementation note: // The order in which the beginning middle and end are added to the final // result matters. - + /// Matches a single pattern in the string in the given range /// /// A pattern may be one three options: /// * A single pattern called match which should be matched - /// * A begin and an end pattern containing an optional body of patterns + /// * A begin and an end pattern containing an optional body of patterns /// which should be matched between the begin and the end - /// * Only a body of patterns without the begin and end. Any pattern may be + /// * Only a body of patterns without the begin and end. Any pattern may be /// matched successfully /// /// - returns: The result of the match. Nil if unsuccessful @@ -174,7 +174,7 @@ public class Parser { guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { return nil } - + let newLocation = NSMaxRange(beginResults.range) guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation) else { return nil @@ -195,8 +195,8 @@ public class Parser { } return nil } - - /// Matches a given regular expression in a String and returns range + + /// Matches a given regular expression in a String and returns range /// information for the captures /// /// - parameter expression: The regular expression to match @@ -210,37 +210,37 @@ public class Parser { /// - returns: The set containing the results. May be nil if the expression /// could not match any part of the string. It may also be empty /// and only contain range information to show what it matched. - private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { + private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { return nil } - - var resultSet = ResultSet(startingRange: result.range) + + var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(scope: baseSelector!, range: result.range)) } - if let captures = captures { - for index in captures.captureIndexes { - let range = result.rangeAtIndex(Int(index)) - if range.location == NSNotFound { - continue - } - - if let scope = captures[index]?.name { - resultSet.addResult(Result(scope: scope, range: range)) - } - } - } - + if let captures = captures { + for index in captures.captureIndexes { + let range = result.rangeAtIndex(Int(index)) + if range.location == NSNotFound { + continue + } + + if let scope = captures[index]?.name { + resultSet.addResult(Result(scope: scope, range: range)) + } + } + } + return resultSet - } + } - private func applyResults(resultSet: ResultSet?, callback: Callback) { - for result in resultSet?.results ?? [] { + private func applyResults(resultSet: ResultSet?, callback: Callback) { + for result in resultSet?.results ?? [] { if result.scope != "" && result.range.length > 0 { callback(scope: result.scope, range: result.range) } - } - } + } + } } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 1fd7389..2f646b6 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -10,78 +10,78 @@ import Foundation final class Pattern { - // MARK: - Properties + // MARK: - Properties - let name: String? + let name: String? let match: NSRegularExpression? - let captures: CaptureCollection? + let captures: CaptureCollection? let begin: NSRegularExpression? - let beginCaptures: CaptureCollection? + let beginCaptures: CaptureCollection? let end: NSRegularExpression? - let endCaptures: CaptureCollection? - private weak var parent: Pattern? - private var patterns: Patterns + let endCaptures: CaptureCollection? + private weak var parent: Pattern? + private var patterns: Patterns - var superpattern: Pattern? { - return parent - } + var superpattern: Pattern? { + return parent + } - var subpatterns: Patterns { - return patterns - } + var subpatterns: Patterns { + return patterns + } - // MARK: - Initializers + // MARK: - Initializers init?(dictionary: [NSObject: AnyObject], repository: Repository, parent: Pattern? = nil) { self.parent = parent self.name = dictionary["name"] as? String - + if let matchExpr = dictionary["match"] as? String { self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] } else { self.match = nil } - + if let beginExpr = dictionary["begin"] as? String { self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) } else { self.begin = nil } - + if let endExpr = dictionary["end"] as? String { self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { self.end = nil } - + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { - self.beginCaptures = CaptureCollection(dictionary: dictionary) - } else { - self.beginCaptures = nil - } - - if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { - self.captures = CaptureCollection(dictionary: dictionary) - } else { - self.captures = nil - } - - if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { + self.beginCaptures = CaptureCollection(dictionary: dictionary) + } else { + self.beginCaptures = nil + } + + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { + self.captures = CaptureCollection(dictionary: dictionary) + } else { + self.captures = nil + } + + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { self.endCaptures = CaptureCollection(dictionary: dictionary) } else { self.endCaptures = nil } - + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { self.patterns = Patterns(array: array, repository: repository) } else { self.patterns = Patterns(array: [], repository: repository) } - + if dictionary["match"] as? String != nil && self.match == nil { return nil } - + if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { return nil } diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift index a9b49d7..e61195d 100644 --- a/SyntaxKit/Patterns.swift +++ b/SyntaxKit/Patterns.swift @@ -10,9 +10,9 @@ import Foundation class Patterns { private var content: [Pattern] - + init(array: [[NSObject: AnyObject]], repository: Repository) { - + content = [] for value in array { if let include = value["include"] as? String { @@ -32,8 +32,8 @@ class Patterns { } } } - + func getContent() -> [Pattern] { return content } -} \ No newline at end of file +} diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 2ced937..630dc6e 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -10,7 +10,7 @@ import Foundation class Repository { private var entries: [String: Pattern] - + init(repo: [String: [NSObject: AnyObject]]) { self.entries = [:] for (key, value) in repo { @@ -29,12 +29,12 @@ class Repository { } } } - + func allEntries() -> [String: Pattern] { return entries } - + subscript(index: String) -> Pattern? { return entries[index] } -} \ No newline at end of file +} diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index be1a604..fd6bd05 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -10,16 +10,16 @@ import Foundation struct Result { - // MARK: - Properties + // MARK: - Properties - let scope: String - let range: NSRange + let scope: String + let range: NSRange - // MARK: - Initializers + // MARK: - Initializers - init(scope: String, range: NSRange) { - self.scope = scope - self.range = range - } + init(scope: String, range: NSRange) { + self.scope = scope + self.range = range + } } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 3d29d49..49975bc 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -10,35 +10,35 @@ import Foundation struct ResultSet { - // MARK: - Properties + // MARK: - Properties - private var _results = [Result]() - var results: [Result] { - return _results - } + private var _results = [Result]() + var results: [Result] { + return _results + } + + var range: NSRange - var range: NSRange + var isEmpty: Bool { + return results.isEmpty + } - var isEmpty: Bool { - return results.isEmpty - } - init(startingRange range: NSRange) { self.range = range } - // MARK: - Adding + // MARK: - Adding - mutating func addResult(result: Result) { - _results.append(result) + mutating func addResult(result: Result) { + _results.append(result) - self.range = NSUnionRange(range, result.range) - } + self.range = NSUnionRange(range, result.range) + } - mutating func addResults(resultSet: ResultSet) { + mutating func addResults(resultSet: ResultSet) { self.range = NSUnionRange(range, resultSet.range) - for result in resultSet.results { - addResult(result) - } - } + for result in resultSet.results { + addResult(result) + } + } } diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 605d2d2..469dc7e 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -11,19 +11,19 @@ import SyntaxKit class AttributedParserTests: XCTestCase { - // MARK: - Properties + // MARK: - Properties - let parser = AttributedParser(language: language("YAML"), theme: simpleTheme()) + let parser = AttributedParser(language: language("YAML"), theme: simpleTheme()) - // MARK: - Tests + // MARK: - Tests - func testParsing() { - let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") + func testParsing() { + let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(0, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "red"] as NSDictionary, string.attributesAtIndex(7, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(19, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "purple"] as NSDictionary, string.attributesAtIndex(25, effectiveRange: nil) as NSDictionary) - } + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(0, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "red"] as NSDictionary, string.attributesAtIndex(7, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(19, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "purple"] as NSDictionary, string.attributesAtIndex(25, effectiveRange: nil) as NSDictionary) + } } diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 0cdce46..9a944d5 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -11,29 +11,29 @@ import XCTest class LanguageTests: XCTestCase { - // MARK: - Properties + // MARK: - Properties - let yaml = language("YAML") + let yaml = language("YAML") - // MARK: - Tests + // MARK: - Tests - func testLoading() { - XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) - XCTAssertEqual("YAML", yaml.name) - XCTAssertEqual("source.yaml", yaml.scopeName) + func testLoading() { + XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) + XCTAssertEqual("YAML", yaml.name) + XCTAssertEqual("source.yaml", yaml.scopeName) - XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns.getContent()[0].name!) + XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns.getContent()[0].name!) XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns.getContent()[0].beginCaptures![0]!.name) XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns.getContent()[0].endCaptures![0]!.name) XCTAssertEqual("punctuation.definition.comment.ruby", yaml.patterns.getContent()[0].subpatterns.getContent()[0].captures![1]!.name) - XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns.getContent()[1].name!) + XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns.getContent()[1].name!) XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns.getContent()[1].beginCaptures![2]!.name) XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns.getContent()[1].beginCaptures![5]!.name) - XCTAssertEqual("constant.numeric.yaml", yaml.patterns.getContent()[2].name!) + XCTAssertEqual("constant.numeric.yaml", yaml.patterns.getContent()[2].name!) - let pattern = yaml.patterns.getContent()[3] - XCTAssertEqual("string.unquoted.yaml", pattern.name!) - XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures![1]!.name) - } + let pattern = yaml.patterns.getContent()[3] + XCTAssertEqual("string.unquoted.yaml", pattern.name!) + XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures![1]!.name) + } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 9191e86..05100fa 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -11,57 +11,57 @@ import SyntaxKit class ParserTests: XCTestCase { - // MARK: - Properties - - let parser = Parser(language: language("YAML")) - - - // MARK: - Tests - - func testParsingBeginEnd() { - var stringQuoted: NSRange? - var punctuationBegin: NSRange? - var punctuationEnd: NSRange? - - parser.parse("title: \"Hello World\"\n") { scope, range in - if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { - stringQuoted = range - } - - if punctuationBegin == nil && scope.hasPrefix("punctuation.definition.string.begin") { - punctuationBegin = range - } - - if punctuationEnd == nil && scope.hasPrefix("punctuation.definition.string.end") { - punctuationEnd = range - } - } - - XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) - XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin) - XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) - } - - func testParsingBeginEndCrap() { - var stringQuoted: NSRange? - - parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { scope, range in - if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { - stringQuoted = range - } - } - - XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) - } - + // MARK: - Properties + + let parser = Parser(language: language("YAML")) + + + // MARK: - Tests + + func testParsingBeginEnd() { + var stringQuoted: NSRange? + var punctuationBegin: NSRange? + var punctuationEnd: NSRange? + + parser.parse("title: \"Hello World\"\n") { scope, range in + if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { + stringQuoted = range + } + + if punctuationBegin == nil && scope.hasPrefix("punctuation.definition.string.begin") { + punctuationBegin = range + } + + if punctuationEnd == nil && scope.hasPrefix("punctuation.definition.string.end") { + punctuationEnd = range + } + } + + XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) + XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin) + XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) + } + + func testParsingBeginEndCrap() { + var stringQuoted: NSRange? + + parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { scope, range in + if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { + stringQuoted = range + } + } + + XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) + } + func testParsingGarbage() { parser.parse("") { _, _ in } parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } } - func testRuby() { - let parser = Parser(language: language("Ruby")) - let input = fixture("test.rb", "txt") - parser.parse(input, match: { _, _ in return }) - } + func testRuby() { + let parser = Parser(language: language("Ruby")) + let input = fixture("test.rb", "txt") + parser.parse(input, match: { _, _ in return }) + } } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 7cb2f22..1896f72 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -11,75 +11,75 @@ import XCTest @testable import SyntaxKit func fixture(name: String, _ type: String) -> String! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: type)! - return try! String(contentsOfFile: path) + let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: type)! + return try! String(contentsOfFile: path) } func language(name: String) -> Language! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! - let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - return Language(dictionary: plist)! + let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! + let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] + return Language(dictionary: plist)! } func theme(name: String) -> Theme! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmTheme")! - let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - return Theme(dictionary: plist)! + let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmTheme")! + let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] + return Theme(dictionary: plist)! } func simpleTheme() -> Theme! { - return Theme(dictionary: [ - "uuid": "7", - "name": "Simple", - "settings": [ - [ - "scope": "entity.name", - "settings": [ - "color": "blue" - ] - ], - [ - "scope": "string", - "settings": [ - "color": "red" - ] - ], - [ - "scope": "constant.numeric", - "settings": [ - "color": "purple" - ] - ] - ] - ]) + return Theme(dictionary: [ + "uuid": "7", + "name": "Simple", + "settings": [ + [ + "scope": "entity.name", + "settings": [ + "color": "blue" + ] + ], + [ + "scope": "string", + "settings": [ + "color": "red" + ] + ], + [ + "scope": "constant.numeric", + "settings": [ + "color": "purple" + ] + ] + ] + ]) } #if os(iOS) import UIKit extension Color { - var redComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(&value, green: nil, blue: nil, alpha: nil) - return value - } + var redComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(&value, green: nil, blue: nil, alpha: nil) + return value + } - var greenComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: &value, blue: nil, alpha: nil) - return value - } + var greenComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: &value, blue: nil, alpha: nil) + return value + } - var blueComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: nil, blue: &value, alpha: nil) - return value - } + var blueComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: nil, blue: &value, alpha: nil) + return value + } - var alphaComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: nil, blue: nil, alpha: &value) - return value - } + var alphaComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: nil, blue: nil, alpha: &value) + return value + } } #endif @@ -88,14 +88,14 @@ func assertEqualColors(color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.0 XCTAssert(false, "colors have to be non-nil") return } - XCTAssertEqualWithAccuracy(color1!.redComponent, color2!.redComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.greenComponent, color2!.greenComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.blueComponent, color2!.blueComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.alphaComponent, color2!.alphaComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.redComponent, color2!.redComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.greenComponent, color2!.greenComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.blueComponent, color2!.blueComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(color1!.alphaComponent, color2!.alphaComponent, accuracy: accuracy) } extension NSRange: Equatable { } public func ==(lhs: NSRange, rhs: NSRange) -> Bool { - return lhs.location == rhs.location && lhs.length == rhs.length + return lhs.location == rhs.location && lhs.length == rhs.length } diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index a539b9a..34fabd4 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -11,20 +11,20 @@ import XCTest class ThemeTests: XCTestCase { - // MARK: - Properties + // MARK: - Properties - let tomorrow = theme("Tomorrow") + let tomorrow = theme("Tomorrow") let solarized = theme("Solarized") - // MARK: - Tests + // MARK: - Tests - func testLoading() { - XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) - XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) + func testLoading() { + XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) + XCTAssertEqual("Tomorrow", tomorrow.name) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) - } - + } + func testComplexTheme() { XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) XCTAssertEqual("Solarized (light)", solarized.name) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index b105522..dbe2b8e 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -9,51 +9,51 @@ import Foundation #if os(iOS) || os(watchOS) - import UIKit + import UIKit #endif public typealias Attributes = [String: AnyObject] public struct Theme { - // MARK: - Properties + // MARK: - Properties - public let UUID: String - public let name: String - public let attributes: [String: Attributes] + public let UUID: String + public let name: String + public let attributes: [String: Attributes] - // MARK: - Initializers + // MARK: - Initializers - public init?(dictionary: [NSObject: AnyObject]) { - guard let UUID = dictionary["uuid"] as? String, - name = dictionary["name"] as? String, - rawSettings = dictionary["settings"] as? [[String: AnyObject]] - else { return nil } + public init?(dictionary: [NSObject: AnyObject]) { + guard let UUID = dictionary["uuid"] as? String, + name = dictionary["name"] as? String, + rawSettings = dictionary["settings"] as? [[String: AnyObject]] + else { return nil } - self.UUID = UUID - self.name = name + self.UUID = UUID + self.name = name - var attributes = [String: Attributes]() - for raw in rawSettings { - guard let scopes = raw["scope"] as? String else { continue } - guard var setting = raw["settings"] as? [String: AnyObject] else { continue } + var attributes = [String: Attributes]() + for raw in rawSettings { + guard let scopes = raw["scope"] as? String else { continue } + guard var setting = raw["settings"] as? [String: AnyObject] else { continue } - if let value = setting.removeValueForKey("foreground") as? String { - setting[NSForegroundColorAttributeName] = Color(hex: value) - } + if let value = setting.removeValueForKey("foreground") as? String { + setting[NSForegroundColorAttributeName] = Color(hex: value) + } - if let value = setting.removeValueForKey("background") as? String { - setting[NSBackgroundColorAttributeName] = Color(hex: value) - } + if let value = setting.removeValueForKey("background") as? String { + setting[NSBackgroundColorAttributeName] = Color(hex: value) + } - // TODO: caret, invisibles, lightHighlight, selection, font style + // TODO: caret, invisibles, lightHighlight, selection, font style - for scope in scopes.componentsSeparatedByString(",") { - let key = scope.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) - attributes[key] = setting - } - } - self.attributes = attributes - } + for scope in scopes.componentsSeparatedByString(",") { + let key = scope.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + attributes[key] = setting + } + } + self.attributes = attributes + } } From da92e2b2872ff21cef8d106bda490688019c4d7e Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 31 Jan 2016 21:21:43 +0100 Subject: [PATCH 016/110] Implement incremental parsing This change adds updatedRangeForChange to the parser and implements a ScopedString class to store all the scope information. Most other changes are tests for the new functionality or minor consistency changes. --- SyntaxKit.xcodeproj/project.pbxproj | 18 ++ ...36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 55 +++- SyntaxKit/AttributedParser.swift | 4 +- SyntaxKit/Language.swift | 1 + SyntaxKit/Parser.swift | 302 +++++++++++++++--- SyntaxKit/Pattern.swift | 19 +- SyntaxKit/ResultSet.swift | 4 + SyntaxKit/ScopedString.swift | 244 ++++++++++++++ SyntaxKit/Tests/IncrementalParsingTests.swift | 92 ++++++ SyntaxKit/Tests/ScopedStringTests.swift | 64 ++++ .../SwiftBaselineHighlightingTests.swift | 2 +- SyntaxKit/Tests/TestHelper.swift | 6 + SyntaxKit/Theme.swift | 32 +- 13 files changed, 779 insertions(+), 64 deletions(-) create mode 100644 SyntaxKit/ScopedString.swift create mode 100644 SyntaxKit/Tests/IncrementalParsingTests.swift create mode 100644 SyntaxKit/Tests/ScopedStringTests.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 6ac470f..418664c 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -58,10 +58,16 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; + 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; + 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; + 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; + 8CE6BE2E1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; + 8CE6BE2F1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; @@ -145,8 +151,11 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; + 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Solarized.tmTheme; sourceTree = ""; }; + 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedStringTests.swift; sourceTree = ""; }; 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Swift.tmLanguage; sourceTree = ""; }; 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patterns.swift; sourceTree = ""; }; @@ -211,6 +220,8 @@ 211989881B2EB8D400F0D786 /* ParserTests.swift */, 210299D01B2E8924009C61EE /* AttributedParserTests.swift */, 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */, + 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */, + 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */, 210299CF1B2E8924009C61EE /* LanguageTests.swift */, 2119898A1B2EBA2C00F0D786 /* ThemeTests.swift */, ); @@ -269,6 +280,7 @@ 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 211989901B2EC38B00F0D786 /* Language.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, + 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */, 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, @@ -513,6 +525,7 @@ 211989C31B2EC40500F0D786 /* Language.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, + 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, @@ -531,6 +544,8 @@ files = ( 211989CF1B2EC40C00F0D786 /* ThemeTests.swift in Sources */, 211989CC1B2EC40C00F0D786 /* ParserTests.swift in Sources */, + 8CE6BE2F1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */, + 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */, 211989CE1B2EC40C00F0D786 /* LanguageTests.swift in Sources */, 211989CB1B2EC40C00F0D786 /* TestHelper.swift in Sources */, 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, @@ -553,6 +568,7 @@ 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, + 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989971B2EC38B00F0D786 /* AttributedParser.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -562,8 +578,10 @@ buildActionMask = 2147483647; files = ( 211989841B2EB18600F0D786 /* TestHelper.swift in Sources */, + 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */, 211989891B2EB8D400F0D786 /* ParserTests.swift in Sources */, 210299DF1B2E892E009C61EE /* AttributedParserTests.swift in Sources */, + 8CE6BE2E1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */, 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */, 210299DE1B2E892E009C61EE /* LanguageTests.swift in Sources */, 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist index b813d33..df048f0 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -4,6 +4,59 @@ classNames + IncrementalParsingTests + + testPerformanceBeginEnd() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.003 + baselineIntegrationDisplayName + Local Baseline + + + testPerformanceEdgeCases() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.0026958 + baselineIntegrationDisplayName + Local Baseline + + + testPerformanceExample() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.002 + baselineIntegrationDisplayName + Local Baseline + + + testPerformanceInScope() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.0037323 + baselineIntegrationDisplayName + Local Baseline + + + testPerformanceSingleLine() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.003 + baselineIntegrationDisplayName + Local Baseline + + + SwiftBaselineHighlightingTests testHighlightingPerformance() @@ -11,7 +64,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.108 + 0.085 baselineIntegrationDisplayName Local Baseline diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 2afeed1..6eba32f 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -28,8 +28,8 @@ public class AttributedParser: Parser { // MARK: - Parsing - public func parse(string: String, match callback: AttributedCallback) { - parse(string) { scope, range in + public func parse(string: String, inRange bounds: NSRange? = nil, match callback: AttributedCallback) { + parse(string, inRange: bounds) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 2445a0a..bf33d49 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -17,6 +17,7 @@ public struct Language { public let scopeName: String let patterns: Patterns + static let globalScope = "GLOBAL" // MARK: - Initializers diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index d16dea0..9626e8a 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -7,6 +7,10 @@ // Turns out TextMate doesn't highlight things it should highlight according to // the grammar so this is not entirely straight forward. // +// It supports incremental parsing. The recommmend use case is to ask the class +// for the range it should be reparsed on the given change. This range can then +// be passed to parsed. +// // Created by Sam Soffes on 9/19/14. Edited by Alexander Hedges // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -24,8 +28,25 @@ public class Parser { public let language: Language -// private var scopesString: NSMutableAttributedString? - + // Contains the string previously passed to parse() if parse has already + // been called. It stores all the associated scopes with hierarchical + // information and ranges. The attributes of the scopes are values of type + // Pattern, the begin/end pattern associated with the scope. The scopes + // might not be fully populated after a call to parse with a limited range. + private var scopesString: ScopedString? + + // Contains information on the previously generated outdated range. This + // property is invalidated after every call to parse. The diff stores + // information on the change that was analysed in outdateRange and is used + // in parse. + // If the inspected change is an addition the string is set to the + // potentially inserted string and the range is set to + // (insertion, length: 0). + // If the inspected change is a deletion the string is nil and the range is + // the range that would potentially be deleted. + private var diff: (String?, NSRange)? + + // MARK: - Initializers public init(language: Language) { @@ -33,18 +54,222 @@ public class Parser { } - // MARK: - Parsing - -// public func scopeForChangeInString(newString: String, atLocation location: Int) -> NSRange { -// return NSRange(location: 0, length: (newString as NSString).length) -// } + // MARK: - Public + + // Algorithmic notes: + // Basically there are four possibilities for the range that has to be + // parsed. + // 1. Change is in the global scope: The line(s) has/have to be reparsed. + // 1. Change is in a block: The body of the block has to be reparsed. + // 1. Change is in header of a block: The entire block has to be reparsed + // 1. Change is in between two blocs or spans multiple blocks: reparse the + // union of the blocks in which the change starts and the one in which + // it ends + + /// Returns the range in the given string that should be re-parsed after the + /// given change. + /// + /// This method returns a range that can be safely passed into parse so that + /// only a part of the string has to be reparsed. + /// In fact passing anything other than this range or nil to parse might + /// lead to uninteded results but is not prohibited. + /// This method is only guaranteed to possibly not return nil if parse was + /// called on the old string before this call. The only kinds of changed + /// supported are single insertions and deletions of strings. + /// + /// - parameter newString: The examined new string. Should be the product + /// of previously parsed + change. + /// - parameter insertion: If the change applied to the old value is an + /// insertion as opposed to a deletion. + /// - parameter range: The range in which the change occurred. In case + /// of an insertion the range in the new string that + /// was inserted. For a deletion it is the range in + /// the old string that was deleted. + /// + /// - returns: A range in newString that can be safely re-parsed. Or nil if + /// everything has to be reparsed. + public func outdatedRangeForChangeInString(newString: String, changeIsInsertion insertion: Bool, changedRange range: NSRange) -> NSRange? { + let s = newString as NSString + if !stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range) { + return nil + } + + let potentialNewString = scopesString!.copy() as! ScopedString + + if insertion { + potentialNewString.insertString(s.substringWithRange(range), atIndex: range.location) + diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) + } else { + potentialNewString.deleteCharactersInRange(range) + diff = (nil, range) + } + if potentialNewString.underlyingString != newString { + return nil + } - public func parse(string: String, inScope scope: NSRange? = nil, match callback: Callback) { - let results = self.matchPatterns(language.patterns, withString: string, withEndPatternFromPattern: nil, startingAtIndex: 0) - self.applyResults(results, callback: callback) + let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(range.location, onlyBodyResults: false) + let scopeAtPreviousIndex = scopesString!.topLevelScopeAtIndex(range.location, onlyBodyResults: false) + let linesRange = s.lineRangeForRange(range) + + if let pattern1 = scopeAtIndex.attribute as! Pattern?, pattern2 = scopeAtPreviousIndex.attribute as! Pattern? { + if pattern1 == pattern2 { + if scopeAtIndex.range.isInBody(range.location) && scopeAtIndex.range.isInBody(range.location - 1) { + return scopeAtIndex.range.bodyRange + } else { + return scopeAtIndex.range.entireRange + } + } else { + if insertion { + assert(false, "dead end") + return nil + } else { + let preceedingScope = scopesString!.topLevelScopeAtIndex(range.location - 1, onlyBodyResults: false) + return NSUnionRange(preceedingScope.range.entireRange, scopeAtIndex.range.entireRange) + } + } + } else if scopeAtIndex.attribute as! Pattern? == nil && scopeAtPreviousIndex.attribute as! Pattern? == nil { + return rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) + } else { + if insertion { + return rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) + } else { + if scopeAtIndex.attribute as! Pattern? == nil { + let preceedingScope = scopesString!.topLevelScopeAtIndex(range.location - 1, onlyBodyResults: false) + let rangeAfter = rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) + return NSUnionRange(preceedingScope.range.entireRange, rangeAfter) + } else { + let rangeBefore = rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) + return NSUnionRange(rangeBefore, scopeAtIndex.range.entireRange) + } + } + } } - + + // Implementation notes: + // The first part tries to find the context in which parsing should take + // place (which block we are in), if any. + // The second part parses the string the until the full range is consumed. + + /// Parses the given string in the given range and calls the callback on + /// every match of a scope + /// + /// If a range is treated more of a recommendation than a requirement. + /// For best results supply a range that was returned from + /// outdatedRangeForChangeInString called before the call to this method. + /// + /// - parameter string: The string that is parsed + /// - parameter range: The range in which the string should be parsed. + /// On nil the entire string will be parsed. + /// - parameter callback: The callback to call on every match of a + /// language scope + public func parse(string: String, var inRange bounds: NSRange? = nil, match callback: Callback) { + var endScope: Scope? = nil + if bounds == nil { + bounds = NSRange(location: 0, length: (string as NSString).length) + scopesString = ScopedString(string: string) + } else if diffRepresentsChangesFromOldStringToNewString(string) { + endScope = self.scopesString!.topLevelScopeAtIndex(bounds!.location, onlyBodyResults: true) + if diff!.0 == nil { + scopesString!.deleteCharactersInRange(diff!.1) + } else { + scopesString!.insertString(diff!.0!, atIndex: diff!.1.location) + } + if scopesString!.underlyingString != string { // recover from inconsistecy (for instance "." shortcut) + print("Used the emergency trick") + bounds = NSRange(location: 0, length: (string as NSString).length) + endScope = nil + scopesString = ScopedString(string: string) + } else { + scopesString!.removeScopesInRange(bounds!) + } + } else { + // here we don't guarantee best results, the user passed in a range we didn't give him + scopesString = ScopedString(string: string) + } + + var startIndex = bounds!.location + let endIndex = NSMaxRange(bounds!) + var allResults = ResultSet(startingRange: bounds!) + allResults.addResult(Result(scope: Language.globalScope, range: bounds!)) + + while startIndex < endIndex { + let endPattern = endScope?.attribute as! Pattern? + let results = self.matchPatterns(endPattern?.subpatterns ?? language.patterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) + + if endScope != nil { + allResults.addResult(Result(scope: endScope!.name, range: results.range)) + } + + if results.range.length != 0 { + if endScope != nil { + endScope = self.scopesString!.lowerScopeForScope(endScope!, AtIndex: startIndex) + } + startIndex = NSMaxRange(results.range) + allResults.addResults(results) + } else { + startIndex = endIndex + } + } + self.applyResults(allResults, callback: callback) + diff = nil + } + + // MARK: - Private + + // MARK: Range Finding Helpers + + // on true: scopeString not nil and is newString before the given change + private func stringChangeIsCompatible(newString: String, isInsertion insertion: Bool, changedRange range: NSRange) -> Bool { + if scopesString == nil { + return false + } + + var oldLength = (newString as NSString).length + if insertion { + oldLength -= range.length + } else { + oldLength += range.length + } + + if (scopesString!.underlyingString as NSString).length != oldLength { + return false + } + return true + } + + private func diffRepresentsChangesFromOldStringToNewString(newStr: NSString) -> Bool { + if diff == nil { + return false + } + if diff!.0 == nil { + if !stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: diff!.1) { + return false + } + } else { + if !stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: diff!.1.location, length: (diff!.0! as NSString).length)) { + return false + } + if newStr.substringWithRange(NSRange(location: diff!.1.location, length: (diff!.0! as NSString).length)) != diff!.0! { + return false + } + } + + return true + } + + /// - returns: The range of the lines starting at the point where the scope + /// begins. + private func rangeTakingPreviousScopeIntoAccount(start: Int, linesRange: NSRange, potentialString: ScopedString) -> NSRange { + for var i = start; i >= linesRange.location; i-- { + if potentialString.topLevelScopeAtIndex(i, onlyBodyResults: false) != potentialString.baseScope { + return NSRange(location: i + 1, length: linesRange.length - (i + 1 - linesRange.location)) + } + } + return linesRange + } + + // MARK: Parsing // Algorithmic notes: // A pattern expression can not match a substring spanning multiple lines @@ -64,59 +289,52 @@ public class Parser { /// - parameter patterns: The patterns to match the sting against /// - parameter string: The matched string /// - parameter endPattern: If specified, the pattern at which to stop - /// the matching process. Otherwise it will just - /// match the entire string. + /// the matching process, overrides stopIndex. + /// On nil it will match up to stopIndex. /// - parameter startingIndex: The index at which to start matching + /// - parameter stopIndex: The index at which to stop matching /// /// - returns: The result set containing the lexical scope names with range - /// information or nil of nothing could be found. - private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int) -> ResultSet? { + /// information. May exceed stopIndex. + private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { assert(endPattern == nil || endPattern!.end != nil) - + let s: NSString = string - let length = s.length - var paragraphStart = startIndex - var paragraphEnd = startIndex - var result: ResultSet? + assert(s.length >= stop) + var lineStart = startIndex + var lineEnd = startIndex + var result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) - while paragraphEnd < length { - s.getLineStart(nil, end: ¶graphEnd, contentsEnd: nil, forRange: NSMakeRange(paragraphEnd, 0)) - var range = NSRange(location: paragraphStart, length: paragraphEnd - paragraphStart) + while lineEnd < stop { + s.getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) + var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) if bestResultForMiddle != nil && !bestResultForMiddle!.isEmpty && bestResultForMiddle!.range.length != 0 { - if result == nil { - result = bestResultForMiddle - } else { - result!.addResults(bestResultForMiddle!) - } + result.addResults(bestResultForMiddle!) let newStart = NSMaxRange(bestResultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) - paragraphEnd = newStart + lineEnd = max(lineEnd, newStart) } if endPattern != nil { - var endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if result != nil { - endMatchResult?.addResults(result!) - } + let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) if endMatchResult != nil { - return endMatchResult + result.addResults(endMatchResult!) + return result } } if bestResultForMiddle == nil || bestResultForMiddle!.isEmpty || bestResultForMiddle!.range.length == 0 { - range = NSRange(location: paragraphEnd, length: 0) + range = NSRange(location: lineEnd, length: 0) } } - paragraphStart = paragraphEnd++ + lineStart = lineEnd++ } - if endPattern != nil { // failed to match end pattern - return nil - } + result.extendWithRange(NSRange(location: startIndex, length: stop - startIndex)) return result } @@ -176,13 +394,13 @@ public class Parser { } let newLocation = NSMaxRange(beginResults.range) - guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation) else { - return nil - } + let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) var result = ResultSet(startingRange: endResults.range) if pattern.name != nil { result.addResult(Result(scope: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } + self.scopesString?.addScopeToBottomWithName(pattern.name ?? "", inRange: HeadedRange(location: beginResults.range.location, headerLength: beginResults.range.length, bodyLength: result.range.length - beginResults.range.length), withAttribute: pattern) +// print("Added scope \(pattern.name) for range: loc: \(beginResults.range.location), headerLen: \(beginResults.range.length), bodyLen: \(result.range.length - beginResults.range.length))") result.addResults(beginResults) result.addResults(endResults) return result diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 2f646b6..6588d6b 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -19,21 +19,15 @@ final class Pattern { let beginCaptures: CaptureCollection? let end: NSRegularExpression? let endCaptures: CaptureCollection? - private weak var parent: Pattern? private var patterns: Patterns - var superpattern: Pattern? { - return parent - } - var subpatterns: Patterns { return patterns } // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject], repository: Repository, parent: Pattern? = nil) { - self.parent = parent + init?(dictionary: [NSObject: AnyObject], repository: Repository) { self.name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { @@ -87,3 +81,14 @@ final class Pattern { } } } + +func ==(lhs: Pattern, rhs: Pattern) -> Bool { + if lhs.name != rhs.name || + lhs.match != rhs.match || + lhs.begin != rhs.begin || + lhs.end != rhs.end || + lhs.patterns.getContent().count != rhs.patterns.getContent().count { + return false + } + return true +} diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 49975bc..ac3e5fe 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -28,6 +28,10 @@ struct ResultSet { } // MARK: - Adding + + mutating func extendWithRange(range: NSRange) { + self.range = NSUnionRange(self.range, range) + } mutating func addResult(result: Result) { _results.append(result) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift new file mode 100644 index 0000000..20a9a71 --- /dev/null +++ b/SyntaxKit/ScopedString.swift @@ -0,0 +1,244 @@ +// +// ScopedString.swift +// SyntaxKit +// +// Created by Alexander Hedges on 29/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import Foundation + +struct HeadedRange { + var location: Int + var headerLength: Int + var bodyLength: Int + + var entireRange: NSRange { + return NSRange(location: location, length: headerLength + bodyLength) + } + + var bodyRange: NSRange { + return NSRange(location: location + headerLength, length: bodyLength) + } + + func isEmpty() -> Bool { + return headerLength == 0 && bodyLength == 0 + } + + func isInBody(index: Int) -> Bool { + return index >= location + headerLength && index <= location + headerLength + bodyLength + } + + func isInHeader(index: Int) -> Bool { + return index >= location && index <= location + headerLength + } + + mutating func subtractRange(range: NSRange) { + bodyLength -= max(0, NSIntersectionRange(range, NSRange(location: location + headerLength, length: bodyLength)).length) + headerLength -= max(0, NSIntersectionRange(range, NSRange(location: location, length: headerLength)).length) + } + + mutating func insertRange(range: NSRange) { + if isInBody(range.location) { + bodyLength += range.length + } else if isInHeader(range.location) { + headerLength += range.length + } + } +} + +func ==(lhs: HeadedRange, rhs: HeadedRange) -> Bool { + return lhs.location == rhs.location && lhs.headerLength == rhs.headerLength && lhs.bodyLength == rhs.bodyLength +} + +struct Scope { + var name: String + var range: HeadedRange + var attribute: AnyObject? +} + +func ==(lhs: Scope, rhs: Scope) -> Bool { + return lhs.name == rhs.name && lhs.range == rhs.range +} + +func !=(lhs: Scope, rhs: Scope) -> Bool { + return !(lhs == rhs) +} + +class ScopedString: NSObject, NSCopying { + + var underlyingString: String + + private var levels: [[Scope]] = [] + + var baseScope: Scope { + return Scope(name: "BaseNameString", range: HeadedRange(location: 0, headerLength: 0, bodyLength: (underlyingString as NSString).length), attribute: nil) + } + + init(string: String) { + self.underlyingString = string + } + + func copyWithZone(zone: NSZone) -> AnyObject { + let newScopedString = ScopedString(string: self.underlyingString) + newScopedString.levels = self.levels + return newScopedString + } + + func numberOfScopes() -> Int { + var sum = 1 + for level in levels { + sum += level.count + } + return sum + } + + func numberOfLevels() -> Int { + return levels.count + 1 + } + + func addScopeToTopWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { + assert(NSIntersectionRange(range.entireRange, baseScope.range.entireRange).length == range.entireRange.length) + let newScope = Scope(name: name, range: range, attribute: attribute) + var added = false + for level in 0..= 0; level-- { + if findScopeIntersectionWithRange(range.entireRange, atLevel: levels[level]) == nil { + levels[level].insert(newScope, atIndex: self.insertionPointForRange(range.entireRange, atLevel: levels[level])) + added = true + break + } + } + if !added { + levels.insert([newScope], atIndex: 0) + } + } + + func topLevelScopeAtIndex(index: Int, onlyBodyResults body: Bool) -> Scope { + assert(index >= 0 && index < baseScope.range.entireRange.length) + let indexRange = NSRange(location: index, length: 1) + for var i = levels.count - 1; i >= 0; i-- { + let level = levels[i] + let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) + if theScope != nil { + if !body || NSIntersectionRange(theScope!.range.bodyRange, indexRange).length != 0 { + return theScope! + } + } + } + return baseScope + } + + func lowerScopeForScope(scope: Scope, AtIndex index: Int) -> Scope { + assert(index >= 0 && index < baseScope.range.entireRange.length) + var foundScope = false + let indexRange = NSRange(location: index, length: 1) + for var i = levels.count - 1; i >= 0; i-- { + let level = levels[i] + let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) + if theScope != nil { + if foundScope { + return scope + } else if theScope! == scope { + foundScope = true + } + } + } + return baseScope + } + + func removeScopesInRange(range: NSRange) { + assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + for var level = levels.count-1; level >= 0; level-- { + for var scope = levels[level].count-1; scope >= 0; scope-- { + let theScope = levels[level][scope] + if NSIntersectionRange(theScope.range.entireRange, range).length == theScope.range.entireRange.length { + levels[level].removeAtIndex(scope) + } + } + if levels[level].count == 0 { + levels.removeAtIndex(level) + } + } + } + + func insertString(string: String, atIndex index: Int) { + assert(index >= 0 && index < baseScope.range.entireRange.length) + let s = underlyingString as NSString + let length = (string as NSString).length + let indexRange = NSRange(location: index, length: 1) + let mutableString = s.mutableCopy() as! NSMutableString + mutableString.insertString(string, atIndex: index) + self.underlyingString = mutableString.copy() as! String + for level in 0.. index { + newScope.range.location += length + } + levels[level][scope] = newScope + } + } + } + + func deleteCharactersInRange(range: NSRange) { + assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString + mutableString.deleteCharactersInRange(range) + self.underlyingString = mutableString.copy() as! String + for var level = levels.count-1; level >= 0; level-- { + for var scope = levels[level].count-1; scope >= 0 ; scope-- { + var theRange = levels[level][scope].range + theRange.subtractRange(range) + if theRange.isEmpty() { + levels[level].removeAtIndex(scope) + } else { + levels[level][scope].range = theRange + } + } + if levels[level].count == 0 { + levels.removeAtIndex(level) + } + } + } + + + // MARK: - Private + + private func findScopeIntersectionWithRange(range: NSRange, atLevel level: [Scope]) -> Scope? { + for scope in level { + if NSIntersectionRange(scope.range.entireRange, range).length != 0 { + return scope + } + } + return nil + } + + private func insertionPointForRange(range: NSRange, atLevel level: [Scope]) -> Int { + var i = 0 + for scope in level { + if range.location < scope.range.entireRange.location { + return i + } + i++ + } + return i + } +} diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift new file mode 100644 index 0000000..b6e98bc --- /dev/null +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -0,0 +1,92 @@ +// +// IncrementalParsingTests.swift +// SyntaxKit +// +// Created by Alexander Hedges on 27/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import XCTest +import SyntaxKit + +class IncrementalParsingTests: XCTestCase { + + let parser = Parser(language: language("Swift")) + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testExample() { + let input = fixture("swifttest.swift", "txt") + + var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) + XCTAssertEqual(rangeToParse, nil) + + parser.parse(input) { _, _ in return } + + var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 5, length: 17)) + + newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 162, length: 2)) + + parser.parse(newInput) { _, _ in return } + + newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 162, length: 1)) + + parser.parse(input) { _, _ in return } + + newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) + XCTAssertEqual(rangeToParse, NSRange(location: 142, length: 23)) + + newInput = stringByReplacingRange(NSRange(location: 159, length: 0), inString: input, withString: "756") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) + XCTAssertEqual(rangeToParse, NSRange(location: 142, length: 23)) + } + + func testPerformanceInScope() { + let input = fixture("swifttest.swift", "txt") + self.parser.parse(input) { _, _ in return } + + self.measureBlock { + let newInput = stringByReplacingRange(NSRange(location: 239, length: 0), inString: input, withString: "Tests") + var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 239, length: 5)) + XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 24)) + + self.parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + + rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: false, changedRange: NSRange(location: 239, length: 5)) + XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 19)) + + self.parser.parse(input, inRange: rangeToParse) { _, _ in return } + } + } + + func testPerformanceEdgeCases() { + let input = fixture("swifttest.swift", "txt") + self.parser.parse(input) { _, _ in return } + + self.measureBlock { + let newInput = stringByReplacingRange(NSRange(location: 139, length: 1), inString: input, withString: "") + var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 139, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 22)) + + self.parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + + rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 139, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) + + self.parser.parse(input, inRange: rangeToParse) { _, _ in return } + } + } +} diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift new file mode 100644 index 0000000..7241c7f --- /dev/null +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -0,0 +1,64 @@ +// +// ScopedStringTests.swift +// SyntaxKit +// +// Created by Alexander Hedges on 30/01/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import XCTest +@testable +import SyntaxKit + +class ScopedStringTests: XCTestCase { + + override func setUp() { + super.setUp() + } + + override func tearDown() { + super.tearDown() + } + + func testScopesString() { + let newScopedString = ScopedString(string: "Test") + XCTAssert(newScopedString.numberOfScopes() == 1) + XCTAssert(newScopedString.numberOfLevels() == 1) + + XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: true) == newScopedString.baseScope) + + let newScope1 = Scope(name: "bogus", range: HeadedRange(location: 1, headerLength: 1, bodyLength: 2), attribute: nil) + newScopedString.addScopeToTopWithName(newScope1.name, inRange: newScope1.range) + XCTAssert(newScopedString.numberOfScopes() == 2) + XCTAssert(newScopedString.numberOfLevels() == 2) + + XCTAssert(newScopedString.topLevelScopeAtIndex(0, onlyBodyResults: true) == newScopedString.baseScope) + XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: true) == newScopedString.baseScope) + XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false) == newScope1) + XCTAssert(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1) == newScopedString.baseScope) + + let newScope2 = Scope(name: "bogus2", range: HeadedRange(location: 2, headerLength: 0, bodyLength: 1), attribute: nil) + newScopedString.addScopeToTopWithName(newScope2.name, inRange: newScope2.range) + XCTAssert(newScopedString.numberOfScopes() == 3) + XCTAssert(newScopedString.numberOfLevels() == 3) + + XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false) == newScope1) + XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: false) == newScope2) + XCTAssert(newScopedString.topLevelScopeAtIndex(3, onlyBodyResults: true) == newScope1) + + XCTAssertFalse(newScopedString.numberOfScopes() == 1) + + newScopedString.deleteCharactersInRange(NSRange(location: 2, length: 1)) + XCTAssert(newScopedString.underlyingString == "Tet") + XCTAssert(newScopedString.numberOfScopes() == 2) + XCTAssert(newScopedString.numberOfLevels() == 2) + + XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false).range == HeadedRange(location: 1, headerLength: 1, bodyLength: 1)) + + newScopedString.insertString("ssssss", atIndex: 2) + XCTAssert(newScopedString.underlyingString == "Tesssssst") + XCTAssert(newScopedString.numberOfScopes() == 2) + + XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: false).range == HeadedRange(location: 1, headerLength: 1, bodyLength: 7)) + } +} diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 269f504..1cb16bf 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -6,7 +6,7 @@ // Copyright © 2016 Sam Soffes. All rights reserved. // -import UIKit +import Foundation import XCTest import SyntaxKit diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 1896f72..7fcab24 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -54,6 +54,12 @@ func simpleTheme() -> Theme! { ]) } +func stringByReplacingRange(range: NSRange, inString string: String, withString inserted: String) -> String { + let newInput = string.mutableCopy() as! NSMutableString + newInput.replaceCharactersInRange(range, withString: inserted) + return newInput.copy() as! String +} + #if os(iOS) import UIKit extension Color { diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index dbe2b8e..43079e2 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -21,8 +21,15 @@ public struct Theme { public let UUID: String public let name: String public let attributes: [String: Attributes] - - + + public var backgroundColor: UIColor { + if let color = attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? UIColor { + return color + } else { + return UIColor.whiteColor() + } + } + // MARK: - Initializers public init?(dictionary: [NSObject: AnyObject]) { @@ -33,25 +40,28 @@ public struct Theme { self.UUID = UUID self.name = name - + var attributes = [String: Attributes]() for raw in rawSettings { - guard let scopes = raw["scope"] as? String else { continue } guard var setting = raw["settings"] as? [String: AnyObject] else { continue } - + if let value = setting.removeValueForKey("foreground") as? String { setting[NSForegroundColorAttributeName] = Color(hex: value) } - + if let value = setting.removeValueForKey("background") as? String { setting[NSBackgroundColorAttributeName] = Color(hex: value) } - + // TODO: caret, invisibles, lightHighlight, selection, font style - - for scope in scopes.componentsSeparatedByString(",") { - let key = scope.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) - attributes[key] = setting + + if let scopes = raw["scope"] as? String { + for scope in scopes.componentsSeparatedByString(",") { + let key = scope.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + attributes[key] = setting + } + } else if !setting.isEmpty { + attributes[Language.globalScope] = setting } } self.attributes = attributes From ec64f83e758f2c8ed1ced9198a2ebaf2ba6e185c Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 31 Jan 2016 22:59:49 +0100 Subject: [PATCH 017/110] greatly simplify parser incremental parsing algorithm --- SyntaxKit/Parser.swift | 79 +++++-------------- SyntaxKit/ScopedString.swift | 4 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 10 +-- SyntaxKit/Tests/ScopedStringTests.swift | 4 +- 4 files changed, 28 insertions(+), 69 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 9626e8a..6d35fd0 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -57,14 +57,9 @@ public class Parser { // MARK: - Public // Algorithmic notes: - // Basically there are four possibilities for the range that has to be - // parsed. - // 1. Change is in the global scope: The line(s) has/have to be reparsed. - // 1. Change is in a block: The body of the block has to be reparsed. - // 1. Change is in header of a block: The entire block has to be reparsed - // 1. Change is in between two blocs or spans multiple blocks: reparse the - // union of the blocks in which the change starts and the one in which - // it ends + // If change occurred in a block reparse the lines in which the change + // happened and the range of the block from this point on. If the change + // occurred in the global scope just reparse the lines that changed. /// Returns the range in the given string that should be re-parsed after the /// given change. @@ -107,41 +102,13 @@ public class Parser { return nil } + let linesRanges = s.lineRangeForRange(range) let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(range.location, onlyBodyResults: false) - let scopeAtPreviousIndex = scopesString!.topLevelScopeAtIndex(range.location, onlyBodyResults: false) - let linesRange = s.lineRangeForRange(range) - - if let pattern1 = scopeAtIndex.attribute as! Pattern?, pattern2 = scopeAtPreviousIndex.attribute as! Pattern? { - if pattern1 == pattern2 { - if scopeAtIndex.range.isInBody(range.location) && scopeAtIndex.range.isInBody(range.location - 1) { - return scopeAtIndex.range.bodyRange - } else { - return scopeAtIndex.range.entireRange - } - } else { - if insertion { - assert(false, "dead end") - return nil - } else { - let preceedingScope = scopesString!.topLevelScopeAtIndex(range.location - 1, onlyBodyResults: false) - return NSUnionRange(preceedingScope.range.entireRange, scopeAtIndex.range.entireRange) - } - } - } else if scopeAtIndex.attribute as! Pattern? == nil && scopeAtPreviousIndex.attribute as! Pattern? == nil { - return rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) + if scopeAtIndex == potentialNewString.baseScope { + return linesRanges } else { - if insertion { - return rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) - } else { - if scopeAtIndex.attribute as! Pattern? == nil { - let preceedingScope = scopesString!.topLevelScopeAtIndex(range.location - 1, onlyBodyResults: false) - let rangeAfter = rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) - return NSUnionRange(preceedingScope.range.entireRange, rangeAfter) - } else { - let rangeBefore = rangeTakingPreviousScopeIntoAccount(range.location - 1, linesRange: linesRange, potentialString: potentialNewString) - return NSUnionRange(rangeBefore, scopeAtIndex.range.entireRange) - } - } + let endOfCurrentScope = NSMaxRange(scopeAtIndex.range.entireRange) + return NSUnionRange(linesRanges, NSRange(location: range.location, length: endOfCurrentScope - range.location)) } } @@ -201,11 +168,11 @@ public class Parser { } if results.range.length != 0 { + allResults.addResults(results) + startIndex = NSMaxRange(results.range) if endScope != nil { endScope = self.scopesString!.lowerScopeForScope(endScope!, AtIndex: startIndex) } - startIndex = NSMaxRange(results.range) - allResults.addResults(results) } else { startIndex = endIndex } @@ -217,15 +184,16 @@ public class Parser { // MARK: - Private - // MARK: Range Finding Helpers + // MARK: Range Helpers - // on true: scopeString not nil and is newString before the given change - private func stringChangeIsCompatible(newString: String, isInsertion insertion: Bool, changedRange range: NSRange) -> Bool { + /// - returns: true if scopeString not nil and the number of characters + /// changed is consistent with the new string + private func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange) -> Bool { if scopesString == nil { return false } - var oldLength = (newString as NSString).length + var oldLength = newString.length if insertion { oldLength -= range.length } else { @@ -238,6 +206,8 @@ public class Parser { return true } + /// - returns: true if diff not nil and predicted change from diff matches + /// the characters from the new string in that range private func diffRepresentsChangesFromOldStringToNewString(newStr: NSString) -> Bool { if diff == nil { return false @@ -257,18 +227,7 @@ public class Parser { return true } - - /// - returns: The range of the lines starting at the point where the scope - /// begins. - private func rangeTakingPreviousScopeIntoAccount(start: Int, linesRange: NSRange, potentialString: ScopedString) -> NSRange { - for var i = start; i >= linesRange.location; i-- { - if potentialString.topLevelScopeAtIndex(i, onlyBodyResults: false) != potentialString.baseScope { - return NSRange(location: i + 1, length: linesRange.length - (i + 1 - linesRange.location)) - } - } - return linesRange - } - + // MARK: Parsing // Algorithmic notes: @@ -399,7 +358,7 @@ public class Parser { if pattern.name != nil { result.addResult(Result(scope: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } - self.scopesString?.addScopeToBottomWithName(pattern.name ?? "", inRange: HeadedRange(location: beginResults.range.location, headerLength: beginResults.range.length, bodyLength: result.range.length - beginResults.range.length), withAttribute: pattern) + self.scopesString?.addScopeAtBottomWithName(pattern.name ?? "", inRange: HeadedRange(location: beginResults.range.location, headerLength: beginResults.range.length, bodyLength: result.range.length - beginResults.range.length), withAttribute: pattern) // print("Added scope \(pattern.name) for range: loc: \(beginResults.range.location), headerLen: \(beginResults.range.length), bodyLen: \(result.range.length - beginResults.range.length))") result.addResults(beginResults) result.addResults(endResults) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 20a9a71..1665ad6 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -97,7 +97,7 @@ class ScopedString: NSObject, NSCopying { return levels.count + 1 } - func addScopeToTopWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { + func addScopeAtTopWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { assert(NSIntersectionRange(range.entireRange, baseScope.range.entireRange).length == range.entireRange.length) let newScope = Scope(name: name, range: range, attribute: attribute) var added = false @@ -113,7 +113,7 @@ class ScopedString: NSObject, NSCopying { } } - func addScopeToBottomWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { + func addScopeAtBottomWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { assert(NSIntersectionRange(range.entireRange, baseScope.range.entireRange).length == range.entireRange.length) let newScope = Scope(name: name, range: range, attribute: attribute) var added = false diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index b6e98bc..0b2405d 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -31,27 +31,27 @@ class IncrementalParsingTests: XCTestCase { var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 5, length: 17)) + XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 19)) newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 162, length: 2)) + XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) parser.parse(newInput) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 162, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) parser.parse(input) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) - XCTAssertEqual(rangeToParse, NSRange(location: 142, length: 23)) + XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) newInput = stringByReplacingRange(NSRange(location: 159, length: 0), inString: input, withString: "756") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) - XCTAssertEqual(rangeToParse, NSRange(location: 142, length: 23)) + XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) } func testPerformanceInScope() { diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 7241c7f..6784fd2 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -28,7 +28,7 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: true) == newScopedString.baseScope) let newScope1 = Scope(name: "bogus", range: HeadedRange(location: 1, headerLength: 1, bodyLength: 2), attribute: nil) - newScopedString.addScopeToTopWithName(newScope1.name, inRange: newScope1.range) + newScopedString.addScopeAtTopWithName(newScope1.name, inRange: newScope1.range) XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) @@ -38,7 +38,7 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1) == newScopedString.baseScope) let newScope2 = Scope(name: "bogus2", range: HeadedRange(location: 2, headerLength: 0, bodyLength: 1), attribute: nil) - newScopedString.addScopeToTopWithName(newScope2.name, inRange: newScope2.range) + newScopedString.addScopeAtTopWithName(newScope2.name, inRange: newScope2.range) XCTAssert(newScopedString.numberOfScopes() == 3) XCTAssert(newScopedString.numberOfLevels() == 3) From e0f444961208f44ad14ba7a6598fa25c46bd963b Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 31 Jan 2016 23:32:19 +0100 Subject: [PATCH 018/110] add some documentation for ScopedString --- SyntaxKit/ScopedString.swift | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 1665ad6..07255c0 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -2,13 +2,33 @@ // ScopedString.swift // SyntaxKit // +// A datastructure that facilitates working with strings that have nested +// scopes associated with them. A scope being a named range that can have an +// attribute assciated with it for the callers convenience. +// The ranges can be nested. The datastrucuture could be visualized like this: +// +// Top: ---- +// ------------- +// ----------------------------- +// Bottom: ---------------------------------------- +// String: "This is (string (with (nest)ed) scopes)!" +// +// Note: +// The bottom-most layer is implicit and is not stored. Unlike shown above the +// ranges of the scopesare actually headed. +// If no layer can actually hold the inserted scope a new layer is added. +// +// The datastructure might be optimized with binary search for insertions at +// the individual levels. +// // Created by Alexander Hedges on 29/01/16. // Copyright © 2016 Sam Soffes. All rights reserved. // import Foundation -struct HeadedRange { +/// A range divided into a head and body part +struct HeadedRange: Equatable { var location: Int var headerLength: Int var bodyLength: Int @@ -51,7 +71,9 @@ func ==(lhs: HeadedRange, rhs: HeadedRange) -> Bool { return lhs.location == rhs.location && lhs.headerLength == rhs.headerLength && lhs.bodyLength == rhs.bodyLength } -struct Scope { + +/// A scope of a +struct Scope: Equatable { var name: String var range: HeadedRange var attribute: AnyObject? @@ -61,9 +83,6 @@ func ==(lhs: Scope, rhs: Scope) -> Bool { return lhs.name == rhs.name && lhs.range == rhs.range } -func !=(lhs: Scope, rhs: Scope) -> Bool { - return !(lhs == rhs) -} class ScopedString: NSObject, NSCopying { @@ -99,6 +118,7 @@ class ScopedString: NSObject, NSCopying { func addScopeAtTopWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { assert(NSIntersectionRange(range.entireRange, baseScope.range.entireRange).length == range.entireRange.length) + let newScope = Scope(name: name, range: range, attribute: attribute) var added = false for level in 0..= 0; level-- { @@ -131,6 +152,7 @@ class ScopedString: NSObject, NSCopying { func topLevelScopeAtIndex(index: Int, onlyBodyResults body: Bool) -> Scope { assert(index >= 0 && index < baseScope.range.entireRange.length) + let indexRange = NSRange(location: index, length: 1) for var i = levels.count - 1; i >= 0; i-- { let level = levels[i] @@ -146,6 +168,7 @@ class ScopedString: NSObject, NSCopying { func lowerScopeForScope(scope: Scope, AtIndex index: Int) -> Scope { assert(index >= 0 && index < baseScope.range.entireRange.length) + var foundScope = false let indexRange = NSRange(location: index, length: 1) for var i = levels.count - 1; i >= 0; i-- { @@ -164,6 +187,7 @@ class ScopedString: NSObject, NSCopying { func removeScopesInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + for var level = levels.count-1; level >= 0; level-- { for var scope = levels[level].count-1; scope >= 0; scope-- { let theScope = levels[level][scope] @@ -179,6 +203,7 @@ class ScopedString: NSObject, NSCopying { func insertString(string: String, atIndex index: Int) { assert(index >= 0 && index < baseScope.range.entireRange.length) + let s = underlyingString as NSString let length = (string as NSString).length let indexRange = NSRange(location: index, length: 1) @@ -200,6 +225,7 @@ class ScopedString: NSObject, NSCopying { func deleteCharactersInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString mutableString.deleteCharactersInRange(range) self.underlyingString = mutableString.copy() as! String From ad2cf92b35f2dc24fff48edfd304045444f7386b Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 31 Jan 2016 23:37:30 +0100 Subject: [PATCH 019/110] minor comment changes --- SyntaxKit/Patterns.swift | 2 +- SyntaxKit/ScopedString.swift | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift index e61195d..411d533 100644 --- a/SyntaxKit/Patterns.swift +++ b/SyntaxKit/Patterns.swift @@ -25,7 +25,7 @@ class Patterns { // self.patterns.append(newSelf) // } } else if include == "$self" { - // TODO: recursive stuff + // TODO: recursive inclusion } } else if let pattern = Pattern(dictionary: value, repository: repository) { content.append(pattern) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 07255c0..f92cde8 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -7,11 +7,11 @@ // attribute assciated with it for the callers convenience. // The ranges can be nested. The datastrucuture could be visualized like this: // -// Top: ---- -// ------------- -// ----------------------------- -// Bottom: ---------------------------------------- -// String: "This is (string (with (nest)ed) scopes)!" +// Top: ---- +// ------------- +// ------- ----------------------------- +// Bottom: ------------------------------------------ +// String: "(This is) (string (with (nest)ed) scopes)!" // // Note: // The bottom-most layer is implicit and is not stored. Unlike shown above the From 13d0071b492bbfeec16e0a061df5dfd40c33f62d Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 1 Feb 2016 19:02:50 +0100 Subject: [PATCH 020/110] Fix range related bugs --- SyntaxKit/Parser.swift | 5 ++- SyntaxKit/ScopedString.swift | 9 +++-- SyntaxKit/Tests/IncrementalParsingTests.swift | 36 ++++++++++++++++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 6d35fd0..d5a0ed9 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -91,18 +91,21 @@ public class Parser { let potentialNewString = scopesString!.copy() as! ScopedString + let linesRanges: NSRange + if insertion { potentialNewString.insertString(s.substringWithRange(range), atIndex: range.location) diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) + linesRanges = s.lineRangeForRange(range) } else { potentialNewString.deleteCharactersInRange(range) diff = (nil, range) + linesRanges = s.lineRangeForRange(NSRange(location: range.location, length: 0)) } if potentialNewString.underlyingString != newString { return nil } - let linesRanges = s.lineRangeForRange(range) let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(range.location, onlyBodyResults: false) if scopeAtIndex == potentialNewString.baseScope { return linesRanges diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index f92cde8..5f326a1 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -56,6 +56,9 @@ struct HeadedRange: Equatable { mutating func subtractRange(range: NSRange) { bodyLength -= max(0, NSIntersectionRange(range, NSRange(location: location + headerLength, length: bodyLength)).length) headerLength -= max(0, NSIntersectionRange(range, NSRange(location: location, length: headerLength)).length) + if (range.location < self.location) { + self.location -= NSIntersectionRange(range, NSRange(location: 0, length: self.location)).length + } } mutating func insertRange(range: NSRange) { @@ -151,7 +154,7 @@ class ScopedString: NSObject, NSCopying { } func topLevelScopeAtIndex(index: Int, onlyBodyResults body: Bool) -> Scope { - assert(index >= 0 && index < baseScope.range.entireRange.length) + assert(index >= 0 && index <= baseScope.range.entireRange.length) let indexRange = NSRange(location: index, length: 1) for var i = levels.count - 1; i >= 0; i-- { @@ -167,7 +170,7 @@ class ScopedString: NSObject, NSCopying { } func lowerScopeForScope(scope: Scope, AtIndex index: Int) -> Scope { - assert(index >= 0 && index < baseScope.range.entireRange.length) + assert(index >= 0 && index <= baseScope.range.entireRange.length) var foundScope = false let indexRange = NSRange(location: index, length: 1) @@ -202,7 +205,7 @@ class ScopedString: NSObject, NSCopying { } func insertString(string: String, atIndex index: Int) { - assert(index >= 0 && index < baseScope.range.entireRange.length) + assert(index >= 0 && index <= baseScope.range.entireRange.length) let s = underlyingString as NSString let length = (string as NSString).length diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 0b2405d..ab53e03 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -21,13 +21,13 @@ class IncrementalParsingTests: XCTestCase { super.tearDown() } - func testExample() { + func testEdits() { let input = fixture("swifttest.swift", "txt") var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) XCTAssertEqual(rangeToParse, nil) - parser.parse(input) { _, _ in return } + parser.parse(input, inRange: rangeToParse) { _, _ in return } var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) @@ -37,13 +37,13 @@ class IncrementalParsingTests: XCTestCase { rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) - parser.parse(newInput) { _, _ in return } + parser.parse(newInput, inRange: rangeToParse) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) - parser.parse(input) { _, _ in return } + parser.parse(input, inRange: rangeToParse) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) @@ -53,6 +53,34 @@ class IncrementalParsingTests: XCTestCase { rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) } + + func testEdgeCase() { + let input = "// test.swift\n/**" + + parser.parse(input) { _, _ in return } + + var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") + var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) + print(newInput) + + parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + + newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) + print(newInput) + + parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + + newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") + rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) + XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) + print(newInput) + + parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + print(newInput) + } func testPerformanceInScope() { let input = fixture("swifttest.swift", "txt") From dfbc3d2a8a89b70123f4c9f91db0b66cc8a88015 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 1 Feb 2016 21:40:09 +0100 Subject: [PATCH 021/110] fix bug where deleting the beginning of a scope leads to an inadequate range --- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 6 ++---- SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift | 2 +- SyntaxKit/Theme.swift | 8 ++++++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index d5a0ed9..8dff1e2 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -106,7 +106,7 @@ public class Parser { return nil } - let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(range.location, onlyBodyResults: false) + let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRanges)-1, onlyBodyResults: false) if scopeAtIndex == potentialNewString.baseScope { return linesRanges } else { diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index ab53e03..5dbe348 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -62,24 +62,22 @@ class IncrementalParsingTests: XCTestCase { var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) - print(newInput) parser.parse(newInput, inRange: rangeToParse) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) - print(newInput) parser.parse(newInput, inRange: rangeToParse) { _, _ in return } newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) - print(newInput) parser.parse(newInput, inRange: rangeToParse) { _, _ in return } - print(newInput) + + parser.parse("") { _, _ in return } } func testPerformanceInScope() { diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 1cb16bf..e2c2c59 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -35,7 +35,7 @@ class SwiftBaselineHighlightingTests: XCTestCase { assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // string literal - print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) +// print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) var stringRange = NSRange() assertEqualColors(Color(hex: "#2aa198"), string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 43079e2..3ebc57a 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -30,6 +30,14 @@ public struct Theme { } } + public var foregroundColor: UIColor { + if let color = attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? UIColor { + return color + } else { + return UIColor.blackColor() + } + } + // MARK: - Initializers public init?(dictionary: [NSObject: AnyObject]) { From 5e98b64b267fde5186314467c0770133ab1426cd Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 1 Feb 2016 22:44:36 +0100 Subject: [PATCH 022/110] Fix bug where cursor would temporarily disappear on deleting a newline --- SyntaxKit/Parser.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 8dff1e2..8e130bc 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -160,7 +160,6 @@ public class Parser { var startIndex = bounds!.location let endIndex = NSMaxRange(bounds!) var allResults = ResultSet(startingRange: bounds!) - allResults.addResult(Result(scope: Language.globalScope, range: bounds!)) while startIndex < endIndex { let endPattern = endScope?.attribute as! Pattern? From 7a1b4c3454f84fc868bfb21b9480f155a9febc78 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 6 Feb 2016 23:32:21 +0100 Subject: [PATCH 023/110] Fix parser again and extend the tmLanguage grammar parsing now recursive structures and the like are read into the datastructure --- SyntaxKit.xcodeproj/project.pbxproj | 16 +++--- SyntaxKit/Include.swift | 27 +++++++++- SyntaxKit/Language.swift | 22 ++++---- SyntaxKit/Parser.swift | 34 ++++++------ SyntaxKit/Pattern.swift | 83 +++++++++++++++++++---------- SyntaxKit/Patterns.swift | 45 ++++++++-------- SyntaxKit/Repository.swift | 26 ++++----- SyntaxKit/ScopedString.swift | 2 +- SyntaxKit/Tests/LanguageTests.swift | 24 ++++----- 9 files changed, 163 insertions(+), 116 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 418664c..5712572 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -150,7 +150,7 @@ 2122A6DE1B22B9320006409B /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -207,6 +207,7 @@ 210299C11B2E8924009C61EE /* Resources */ = { isa = PBXGroup; children = ( + 8C08C3C61C36FD6D00D8548F /* Color.swift */, 210299C21B2E8924009C61EE /* Info.plist */, ); path = Resources; @@ -276,19 +277,18 @@ children = ( 211989951B2EC38B00F0D786 /* SyntaxKit.h */, 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, - 2119898D1B2EC38B00F0D786 /* Capture.swift */, - 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, - 211989901B2EC38B00F0D786 /* Language.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, - 211989921B2EC38B00F0D786 /* Pattern.swift */, + 211989901B2EC38B00F0D786 /* Language.swift */, 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */, - 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, + 211989921B2EC38B00F0D786 /* Pattern.swift */, 8CEEC0E51C4120A900BF3E85 /* Include.swift */, - 211989931B2EC38B00F0D786 /* Result.swift */, + 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, + 2119898D1B2EC38B00F0D786 /* Capture.swift */, + 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, 211989941B2EC38B00F0D786 /* ResultSet.swift */, + 211989931B2EC38B00F0D786 /* Result.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, - 8C08C3C61C36FD6D00D8548F /* Color.swift */, 210299C11B2E8924009C61EE /* Resources */, 210299CD1B2E8924009C61EE /* Tests */, ); diff --git a/SyntaxKit/Include.swift b/SyntaxKit/Include.swift index 886343b..622ac4c 100644 --- a/SyntaxKit/Include.swift +++ b/SyntaxKit/Include.swift @@ -10,6 +10,29 @@ import Foundation -class Include { - +class Include: Pattern { + + private let referenceName: String + private var associatedRepository: Repository? + + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { + self.referenceName = reference + self.associatedRepository = repository + super.init() + self.parent = parent + } + + func resolveReferenceWithRepository(repository: Repository, inLanguage language: Language) { + if referenceName.hasPrefix("#") { + let key = referenceName.substringFromIndex(referenceName.startIndex.successor()) + if let pattern = (associatedRepository ?? repository)[key] { + self.replaceWithPattern(pattern) + } + } else if referenceName == "$self" { + self.parent!.patterns = language.patterns + } else { + // TODO: import from other language + } + self.associatedRepository = nil + } } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index bf33d49..78016d9 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -15,7 +15,7 @@ public struct Language { public let UUID: String public let name: String public let scopeName: String - let patterns: Patterns + var patterns: [Pattern] static let globalScope = "GLOBAL" @@ -24,24 +24,24 @@ public struct Language { public init?(dictionary: [NSObject: AnyObject]) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, - scopeName = dictionary["scopeName"] as? String + scopeName = dictionary["scopeName"] as? String, + array = dictionary["patterns"] as? [[NSObject: AnyObject]] else { return nil } self.UUID = UUID self.name = name self.scopeName = scopeName - + Patterns.reset() + self.patterns = Patterns.patternsForArray(array, inRepository: nil, caller: nil) + let repository: Repository if let repo = dictionary["repository"] as? [String: [NSObject: AnyObject]] { - repository = Repository(repo: repo) - } else { - repository = Repository(repo: [:]) - } - - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - self.patterns = Patterns(array: array, repository: repository) + repository = Repository(repo: repo, inParent: nil, inLanguage: self) } else { - self.patterns = Patterns(array: [], repository: repository) + repository = Repository(repo: [:], inParent: nil, inLanguage: self) } + + Patterns.resolveReferencesWithRepository(repository, inLanguage: self) + Patterns.reset() } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 8e130bc..4d81c1c 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -257,7 +257,7 @@ public class Parser { /// /// - returns: The result set containing the lexical scope names with range /// information. May exceed stopIndex. - private func matchPatterns(patterns: Patterns, withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { + private func matchPatterns(patterns: [Pattern], withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { assert(endPattern == nil || endPattern!.end != nil) let s: NSString = string @@ -272,26 +272,25 @@ public class Parser { while range.length > 0 { let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) - - if bestResultForMiddle != nil && !bestResultForMiddle!.isEmpty && bestResultForMiddle!.range.length != 0 { - result.addResults(bestResultForMiddle!) - let newStart = NSMaxRange(bestResultForMiddle!.range) - range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) - lineEnd = max(lineEnd, newStart) - } - + if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil { + if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location < bestResultForMiddle!.range.location) { result.addResults(endMatchResult!) return result } } - if bestResultForMiddle == nil || bestResultForMiddle!.isEmpty || bestResultForMiddle!.range.length == 0 { - range = NSRange(location: lineEnd, length: 0) + if bestResultForMiddle != nil && bestResultForMiddle!.range.length != 0 { + result.addResults(bestResultForMiddle!) + let newStart = NSMaxRange(bestResultForMiddle!.range) + range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) + lineEnd = max(lineEnd, newStart) + } else { + break } } + lineStart = lineEnd++ } @@ -315,9 +314,9 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: Patterns, inString string: String, inRange range: NSRange) -> ResultSet? { + private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange range: NSRange) -> ResultSet? { var bestResultForMiddle: ResultSet? - for pattern in patterns.getContent() { + for pattern in patterns { let currRes = self.matchPattern(pattern, inString: string, inRange: range) if currRes?.range.location == range.location { return currRes @@ -361,11 +360,10 @@ public class Parser { result.addResult(Result(scope: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } self.scopesString?.addScopeAtBottomWithName(pattern.name ?? "", inRange: HeadedRange(location: beginResults.range.location, headerLength: beginResults.range.length, bodyLength: result.range.length - beginResults.range.length), withAttribute: pattern) -// print("Added scope \(pattern.name) for range: loc: \(beginResults.range.location), headerLen: \(beginResults.range.length), bodyLen: \(result.range.length - beginResults.range.length))") result.addResults(beginResults) result.addResults(endResults) return result - } else if pattern.subpatterns.getContent().count >= 1 { + } else if pattern.subpatterns.count >= 1 { var result = findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) if pattern.name != nil { result?.addResult(Result(scope: pattern.name!, range: result!.range)) @@ -401,6 +399,10 @@ public class Parser { if let captures = captures { for index in captures.captureIndexes { + if result.numberOfRanges <= Int(index) { + print("Attention unexpected expression: \(regularExpression.pattern)") + continue + } let range = result.rangeAtIndex(Int(index)) if range.location == NSNotFound { continue diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 6588d6b..48fb2a9 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -8,86 +8,113 @@ import Foundation -final class Pattern { +class Pattern { // MARK: - Properties - let name: String? - let match: NSRegularExpression? - let captures: CaptureCollection? - let begin: NSRegularExpression? - let beginCaptures: CaptureCollection? - let end: NSRegularExpression? - let endCaptures: CaptureCollection? - private var patterns: Patterns - - var subpatterns: Patterns { + var name: String? + var match: NSRegularExpression? + var captures: CaptureCollection? + var begin: NSRegularExpression? + var beginCaptures: CaptureCollection? + var end: NSRegularExpression? + var endCaptures: CaptureCollection? + var parent: Pattern? + var patterns: [Pattern] + + var subpatterns: [Pattern] { return patterns } - + // MARK: - Initializers + + init() { + self.name = nil + self.match = nil + self.captures = nil + self.begin = nil + self.beginCaptures = nil + self.end = nil + self.endCaptures = nil + self.patterns = [] - init?(dictionary: [NSObject: AnyObject], repository: Repository) { + } + + init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?) { + self.parent = parent self.name = dictionary["name"] as? String - + if let matchExpr = dictionary["match"] as? String { self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] } else { self.match = nil } - + if let beginExpr = dictionary["begin"] as? String { self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) } else { self.begin = nil } - + if let endExpr = dictionary["end"] as? String { self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { self.end = nil } - + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { self.beginCaptures = CaptureCollection(dictionary: dictionary) } else { self.beginCaptures = nil } - + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { self.captures = CaptureCollection(dictionary: dictionary) } else { self.captures = nil } - + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { self.endCaptures = CaptureCollection(dictionary: dictionary) } else { self.endCaptures = nil } - + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - self.patterns = Patterns(array: array, repository: repository) + self.patterns = [] + self.patterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) } else { - self.patterns = Patterns(array: [], repository: repository) + self.patterns = [] } - + if dictionary["match"] as? String != nil && self.match == nil { return nil + } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + return nil } - - if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + + if self.match == nil && self.begin == nil && self.end == nil && (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { return nil } } + + func replaceWithPattern(pattern: Pattern) { + self.name = pattern.name + self.match = pattern.match + self.captures = pattern.captures + self.begin = pattern.begin + self.beginCaptures = pattern.beginCaptures + self.end = pattern.end + self.endCaptures = pattern.endCaptures + self.patterns = pattern.patterns ?? [] + } } func ==(lhs: Pattern, rhs: Pattern) -> Bool { - if lhs.name != rhs.name || + if lhs.name != rhs.name || lhs.match != rhs.match || lhs.begin != rhs.begin || - lhs.end != rhs.end || - lhs.patterns.getContent().count != rhs.patterns.getContent().count { + lhs.end != rhs.end { return false } return true diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift index 411d533..3e9238b 100644 --- a/SyntaxKit/Patterns.swift +++ b/SyntaxKit/Patterns.swift @@ -9,31 +9,30 @@ import Foundation class Patterns { - private var content: [Pattern] - - init(array: [[NSObject: AnyObject]], repository: Repository) { - - content = [] - for value in array { - if let include = value["include"] as? String { - if include.hasPrefix("#") { - let key = include.substringFromIndex(include.startIndex.successor()) - if let pattern = repository[key] { - content.append(pattern) - } //else if key == self.name { -// let newSelf = Pattern(pattern: self, parent: nil) -// self.patterns.append(newSelf) -// } - } else if include == "$self" { - // TODO: recursive inclusion - } - } else if let pattern = Pattern(dictionary: value, repository: repository) { - content.append(pattern) + + private static var includes: [Include] = [] + + class func reset() { + self.includes = [] + } + + class func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { + var results: [Pattern] = [] + for rawPattern in patterns { + if let include = rawPattern["include"] as? String { + let reference = Include(reference: include, inRepository: repository, parent: caller) + self.includes.append(reference) + results.append(reference) + } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, withRepository: repository) { + results.append(pattern) } } + return results } - - func getContent() -> [Pattern] { - return content + + class func resolveReferencesWithRepository(repository: Repository, inLanguage language: Language) { + for include in self.includes { + include.resolveReferenceWithRepository(repository, inLanguage: language) + } } } diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 630dc6e..42f437b 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -10,31 +10,27 @@ import Foundation class Repository { private var entries: [String: Pattern] + private weak var parentRepository: Repository? - init(repo: [String: [NSObject: AnyObject]]) { + init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, inLanguage language: Language) { self.entries = [:] + self.parentRepository = parent + for (key, value) in repo { + var subRepo: Repository? if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { - let newEntries = NSMutableDictionary(dictionary: self.entries) - newEntries.addEntriesFromDictionary(Repository(repo: containedRepo).allEntries()) - self.entries = newEntries.copy() as! [String: Pattern] + subRepo = Repository(repo: containedRepo, inParent: self, inLanguage: language) } - if let pattern = Pattern(dictionary: value, repository: self) { - self.entries[key] = pattern - } - } - for (key, value) in repo { // now that we have all dependencies, try again - if let pattern = Pattern(dictionary: value, repository: self) { + if let pattern = Pattern(dictionary: value, parent: nil, withRepository: subRepo) { self.entries[key] = pattern } } } - func allEntries() -> [String: Pattern] { - return entries - } - subscript(index: String) -> Pattern? { - return entries[index] + if let resultAtLevel = entries[index] { + return resultAtLevel + } + return parentRepository?[index] } } diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 5f326a1..05494d8 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -75,7 +75,7 @@ func ==(lhs: HeadedRange, rhs: HeadedRange) -> Bool { } -/// A scope of a +/// A Lexical Scope (Not strictly a lexical scope but can be used to represent one) struct Scope: Equatable { var name: String var range: HeadedRange diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 9a944d5..6af0ed1 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -23,17 +23,17 @@ class LanguageTests: XCTestCase { XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) - XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns.getContent()[0].name!) - XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns.getContent()[0].beginCaptures![0]!.name) - XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns.getContent()[0].endCaptures![0]!.name) - XCTAssertEqual("punctuation.definition.comment.ruby", yaml.patterns.getContent()[0].subpatterns.getContent()[0].captures![1]!.name) - XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns.getContent()[1].name!) - XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns.getContent()[1].beginCaptures![2]!.name) - XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns.getContent()[1].beginCaptures![5]!.name) - XCTAssertEqual("constant.numeric.yaml", yaml.patterns.getContent()[2].name!) - - let pattern = yaml.patterns.getContent()[3] - XCTAssertEqual("string.unquoted.yaml", pattern.name!) - XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures![1]!.name) + XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns[0].name) + XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns[0].beginCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns[0].endCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.comment.ruby", yaml.patterns[0].subpatterns[0].captures?[1]?.name) + XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns[1].name) + XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns[1].beginCaptures?[2]?.name) + XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns[1].beginCaptures?[5]?.name) + XCTAssertEqual("constant.numeric.yaml", yaml.patterns[2].name) + + let pattern = yaml.patterns[3] + XCTAssertEqual("string.unquoted.yaml", pattern.name) + XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) } } From 878b5e2d4a2b0d15fe26872a771aba1bdd24fb66 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 12 Feb 2016 11:59:02 +0100 Subject: [PATCH 024/110] Simplify ScopedString interphase and parsing improvements Firstly this should clear up some confusion about scopes. In this project scope means some form of lexical scope (basically what is between the begin and end pattern) while what TextMate calles scope selectors or scope for short is now called patternIdentifier or identifier for short. A scope is now also a type of result. Added test for the range extensions. The parser now returns the range that definitely has to be parsed but parses further if it has to. Turns out the range cannot be determined without actually parsing the new string so this seems to be a good tradeoff. --- SyntaxKit.xcodeproj/project.pbxproj | 8 + SyntaxKit/Parser.swift | 60 +++++--- SyntaxKit/Result.swift | 14 +- SyntaxKit/ResultSet.swift | 11 +- SyntaxKit/Scope.swift | 24 +++ SyntaxKit/ScopedString.swift | 145 ++++++++---------- SyntaxKit/Tests/IncrementalParsingTests.swift | 2 +- SyntaxKit/Tests/ScopedStringTests.swift | 65 ++++++-- SyntaxKit/Theme.swift | 6 +- 9 files changed, 205 insertions(+), 130 deletions(-) create mode 100644 SyntaxKit/Scope.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 5712572..11f9651 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -58,6 +58,9 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8C6C24C81C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; + 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; + 8C6C24CA1C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -151,6 +154,7 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; + 8C6C24C71C6BD66A0087E156 /* Scope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scope.swift; sourceTree = ""; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -288,6 +292,7 @@ 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, 211989941B2EC38B00F0D786 /* ResultSet.swift */, 211989931B2EC38B00F0D786 /* Result.swift */, + 8C6C24C71C6BD66A0087E156 /* Scope.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, 210299C11B2E8924009C61EE /* Resources */, 210299CD1B2E8924009C61EE /* Tests */, @@ -525,6 +530,7 @@ 211989C31B2EC40500F0D786 /* Language.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, + 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */, 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */, @@ -565,6 +571,7 @@ 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, + 8C6C24C81C6BD66A0087E156 /* Scope.swift in Sources */, 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, @@ -596,6 +603,7 @@ 2198CED31B36D5DE00BD463F /* Capture.swift in Sources */, 2198CED61B36D5DE00BD463F /* Language.swift in Sources */, 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */, + 8C6C24CA1C6BD66A0087E156 /* Scope.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 4d81c1c..0b9d136 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -91,27 +91,26 @@ public class Parser { let potentialNewString = scopesString!.copy() as! ScopedString - let linesRanges: NSRange - + let linesRange: NSRange if insertion { potentialNewString.insertString(s.substringWithRange(range), atIndex: range.location) diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) - linesRanges = s.lineRangeForRange(range) + linesRange = s.lineRangeForRange(range) } else { potentialNewString.deleteCharactersInRange(range) diff = (nil, range) - linesRanges = s.lineRangeForRange(NSRange(location: range.location, length: 0)) + linesRange = s.lineRangeForRange(NSRange(location: range.location, length: 0)) } if potentialNewString.underlyingString != newString { return nil } - let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRanges)-1, onlyBodyResults: false) + let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRange) - 1) if scopeAtIndex == potentialNewString.baseScope { - return linesRanges + return linesRange } else { - let endOfCurrentScope = NSMaxRange(scopeAtIndex.range.entireRange) - return NSUnionRange(linesRanges, NSRange(location: range.location, length: endOfCurrentScope - range.location)) + let endOfCurrentScope = NSMaxRange(scopeAtIndex.range) + return NSUnionRange(linesRange, NSRange(location: range.location, length: endOfCurrentScope - range.location)) } } @@ -138,7 +137,7 @@ public class Parser { bounds = NSRange(location: 0, length: (string as NSString).length) scopesString = ScopedString(string: string) } else if diffRepresentsChangesFromOldStringToNewString(string) { - endScope = self.scopesString!.topLevelScopeAtIndex(bounds!.location, onlyBodyResults: true) + endScope = self.scopesString!.topLevelScopeAtIndex(bounds!.location) if diff!.0 == nil { scopesString!.deleteCharactersInRange(diff!.1) } else { @@ -149,16 +148,15 @@ public class Parser { bounds = NSRange(location: 0, length: (string as NSString).length) endScope = nil scopesString = ScopedString(string: string) - } else { - scopesString!.removeScopesInRange(bounds!) } } else { // here we don't guarantee best results, the user passed in a range we didn't give him scopesString = ScopedString(string: string) + print("Warning: No guarantee for optimal results") } var startIndex = bounds!.location - let endIndex = NSMaxRange(bounds!) + var endIndex = NSMaxRange(bounds!) var allResults = ResultSet(startingRange: bounds!) while startIndex < endIndex { @@ -166,7 +164,7 @@ public class Parser { let results = self.matchPatterns(endPattern?.subpatterns ?? language.patterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) if endScope != nil { - allResults.addResult(Result(scope: endScope!.name, range: results.range)) + allResults.addResult(Result(identifier: endScope!.patternIdentifier, range: results.range)) } if results.range.length != 0 { @@ -178,7 +176,15 @@ public class Parser { } else { startIndex = endIndex } + + if startIndex > endIndex && scopesString!.isInString(startIndex + 1) { + let scopeAtIndex = scopesString!.topLevelScopeAtIndex(startIndex + 1) + if endScope == nil && scopesString!.levelForScope(scopeAtIndex) > 0 || endScope != nil && scopesString!.levelForScope(scopeAtIndex) > scopesString!.levelForScope(endScope!) { + endIndex = NSMaxRange(scopeAtIndex.range) + } + } } + scopesString!.removeScopesInRange(allResults.range) self.applyResults(allResults, callback: callback) diff = nil } @@ -203,6 +209,7 @@ public class Parser { } if (scopesString!.underlyingString as NSString).length != oldLength { + print("Warning: incompatible change") return false } return true @@ -212,6 +219,7 @@ public class Parser { /// the characters from the new string in that range private func diffRepresentsChangesFromOldStringToNewString(newStr: NSString) -> Bool { if diff == nil { + print("Warning: Diff is nil") return false } if diff!.0 == nil { @@ -223,6 +231,7 @@ public class Parser { return false } if newStr.substringWithRange(NSRange(location: diff!.1.location, length: (diff!.0! as NSString).length)) != diff!.0! { + print("Warning: Passed in a wierd string") return false } } @@ -357,16 +366,16 @@ public class Parser { let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) var result = ResultSet(startingRange: endResults.range) if pattern.name != nil { - result.addResult(Result(scope: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) + result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } - self.scopesString?.addScopeAtBottomWithName(pattern.name ?? "", inRange: HeadedRange(location: beginResults.range.location, headerLength: beginResults.range.length, bodyLength: result.range.length - beginResults.range.length), withAttribute: pattern) + result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: beginResults.range.location + beginResults.range.length, length: result.range.length - beginResults.range.length), attribute: pattern)) result.addResults(beginResults) result.addResults(endResults) return result } else if pattern.subpatterns.count >= 1 { var result = findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) if pattern.name != nil { - result?.addResult(Result(scope: pattern.name!, range: result!.range)) + result?.addResult(Result(identifier: pattern.name!, range: result!.range)) } return result } @@ -394,7 +403,7 @@ public class Parser { var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { - resultSet.addResult(Result(scope: baseSelector!, range: result.range)) + resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) } if let captures = captures { @@ -409,18 +418,25 @@ public class Parser { } if let scope = captures[index]?.name { - resultSet.addResult(Result(scope: scope, range: range)) + resultSet.addResult(Result(identifier: scope, range: range)) } } } return resultSet } - + private func applyResults(resultSet: ResultSet?, callback: Callback) { - for result in resultSet?.results ?? [] { - if result.scope != "" && result.range.length > 0 { - callback(scope: result.scope, range: result.range) + guard let results = resultSet else { + return + } + + callback(scope: Language.globalScope, range: results.range) + for result in results.results where result.patternIdentifier != "" && result.range.length > 0 { + if let scope = result as? Scope { + self.scopesString?.addScopeAtBottom(scope) + } else { + callback(scope: result.patternIdentifier, range: result.range) } } } diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index fd6bd05..11c5da8 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -8,18 +8,22 @@ import Foundation -struct Result { +class Result: Equatable { // MARK: - Properties - let scope: String - let range: NSRange + let patternIdentifier: String + var range: NSRange // MARK: - Initializers - init(scope: String, range: NSRange) { - self.scope = scope + init(identifier: String, range: NSRange) { + self.patternIdentifier = identifier self.range = range } } + +func ==(lhs: Result, rhs: Result) -> Bool { + return lhs.patternIdentifier == rhs.patternIdentifier && lhs.range.toRange() == rhs.range.toRange() +} diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index ac3e5fe..4f451f8 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -22,11 +22,15 @@ struct ResultSet { var isEmpty: Bool { return results.isEmpty } + + // MARK: - Initializers + init(startingRange range: NSRange) { self.range = range } + // MARK: - Adding mutating func extendWithRange(range: NSRange) { @@ -35,14 +39,13 @@ struct ResultSet { mutating func addResult(result: Result) { _results.append(result) - - self.range = NSUnionRange(range, result.range) + self.extendWithRange(result.range) } mutating func addResults(resultSet: ResultSet) { - self.range = NSUnionRange(range, resultSet.range) + self.extendWithRange(resultSet.range) for result in resultSet.results { - addResult(result) + _results.append(result) } } } diff --git a/SyntaxKit/Scope.swift b/SyntaxKit/Scope.swift new file mode 100644 index 0000000..2e05ec7 --- /dev/null +++ b/SyntaxKit/Scope.swift @@ -0,0 +1,24 @@ +// +// Scope.swift +// SyntaxKit +// +// Created by Alexander Hedges on 10/02/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import Foundation + +class Scope: Result { + + // MARK: - Properties + + let attribute: AnyObject? + + + // MARK: - Initializers + + init(identifier: String, range: NSRange, attribute: AnyObject? = nil) { + self.attribute = attribute + super.init(identifier: identifier, range: range) + } +} diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 05494d8..b0d2362 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -14,9 +14,9 @@ // String: "(This is) (string (with (nest)ed) scopes)!" // // Note: -// The bottom-most layer is implicit and is not stored. Unlike shown above the -// ranges of the scopesare actually headed. -// If no layer can actually hold the inserted scope a new layer is added. +// The bottom-most layer is implicit and is not stored. +// If no layer can hold the inserted scope without intersections a new layer is +// added. // // The datastructure might be optimized with binary search for insertions at // the individual levels. @@ -27,66 +27,32 @@ import Foundation -/// A range divided into a head and body part -struct HeadedRange: Equatable { - var location: Int - var headerLength: Int - var bodyLength: Int - - var entireRange: NSRange { - return NSRange(location: location, length: headerLength + bodyLength) - } - - var bodyRange: NSRange { - return NSRange(location: location + headerLength, length: bodyLength) - } +extension NSRange { func isEmpty() -> Bool { - return headerLength == 0 && bodyLength == 0 + return length == 0 } - func isInBody(index: Int) -> Bool { - return index >= location + headerLength && index <= location + headerLength + bodyLength - } - - func isInHeader(index: Int) -> Bool { - return index >= location && index <= location + headerLength + func containsIndex(index: Int) -> Bool { + return index >= location && index <= location + length } mutating func subtractRange(range: NSRange) { - bodyLength -= max(0, NSIntersectionRange(range, NSRange(location: location + headerLength, length: bodyLength)).length) - headerLength -= max(0, NSIntersectionRange(range, NSRange(location: location, length: headerLength)).length) + length -= NSIntersectionRange(range, NSRange(location: location, length: length)).length if (range.location < self.location) { self.location -= NSIntersectionRange(range, NSRange(location: 0, length: self.location)).length } } mutating func insertRange(range: NSRange) { - if isInBody(range.location) { - bodyLength += range.length - } else if isInHeader(range.location) { - headerLength += range.length + if self.containsIndex(range.location) && range.location < NSMaxRange(self) { + length += range.length + } else if location > range.location { + location += range.length } } } -func ==(lhs: HeadedRange, rhs: HeadedRange) -> Bool { - return lhs.location == rhs.location && lhs.headerLength == rhs.headerLength && lhs.bodyLength == rhs.bodyLength -} - - -/// A Lexical Scope (Not strictly a lexical scope but can be used to represent one) -struct Scope: Equatable { - var name: String - var range: HeadedRange - var attribute: AnyObject? -} - -func ==(lhs: Scope, rhs: Scope) -> Bool { - return lhs.name == rhs.name && lhs.range == rhs.range -} - - class ScopedString: NSObject, NSCopying { var underlyingString: String @@ -94,7 +60,7 @@ class ScopedString: NSObject, NSCopying { private var levels: [[Scope]] = [] var baseScope: Scope { - return Scope(name: "BaseNameString", range: HeadedRange(location: 0, headerLength: 0, bodyLength: (underlyingString as NSString).length), attribute: nil) + return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (underlyingString as NSString).length), attribute: nil) } init(string: String) { @@ -103,7 +69,15 @@ class ScopedString: NSObject, NSCopying { func copyWithZone(zone: NSZone) -> AnyObject { let newScopedString = ScopedString(string: self.underlyingString) - newScopedString.levels = self.levels + newScopedString.levels = [] + for level in levels { + var newLevel: [Scope] = [] + for scope in level { + let newScope = Scope(identifier: scope.patternIdentifier, range: scope.range, attribute: scope.attribute) + newLevel.append(newScope) + } + newScopedString.levels.append(newLevel) + } return newScopedString } @@ -119,58 +93,57 @@ class ScopedString: NSObject, NSCopying { return levels.count + 1 } - func addScopeAtTopWithName(name: String, inRange range: HeadedRange, withAttribute attribute: AnyObject? = nil) { - assert(NSIntersectionRange(range.entireRange, baseScope.range.entireRange).length == range.entireRange.length) + func isInString(index: Int) -> Bool { + return index >= 0 && index <= baseScope.range.length + } + + func addScopeAtTop(scope: Scope) { + assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) - let newScope = Scope(name: name, range: range, attribute: attribute) var added = false for level in 0..= 0; level-- { - if findScopeIntersectionWithRange(range.entireRange, atLevel: levels[level]) == nil { - levels[level].insert(newScope, atIndex: self.insertionPointForRange(range.entireRange, atLevel: levels[level])) + if findScopeIntersectionWithRange(scope.range, atLevel: levels[level]) == nil { + levels[level].insert(scope, atIndex: self.insertionPointForRange(scope.range, atLevel: levels[level])) added = true break } } if !added { - levels.insert([newScope], atIndex: 0) + levels.insert([scope], atIndex: 0) } } - func topLevelScopeAtIndex(index: Int, onlyBodyResults body: Bool) -> Scope { - assert(index >= 0 && index <= baseScope.range.entireRange.length) + func topLevelScopeAtIndex(index: Int) -> Scope { + assert(index >= 0 && index <= baseScope.range.length) let indexRange = NSRange(location: index, length: 1) for var i = levels.count - 1; i >= 0; i-- { let level = levels[i] - let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) - if theScope != nil { - if !body || NSIntersectionRange(theScope!.range.bodyRange, indexRange).length != 0 { - return theScope! - } + if let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) { + return theScope } } return baseScope } func lowerScopeForScope(scope: Scope, AtIndex index: Int) -> Scope { - assert(index >= 0 && index <= baseScope.range.entireRange.length) + assert(index >= 0 && index <= baseScope.range.length) var foundScope = false let indexRange = NSRange(location: index, length: 1) @@ -188,13 +161,28 @@ class ScopedString: NSObject, NSCopying { return baseScope } + func levelForScope(scope: Scope) -> Int { + for var i = 0; i < levels.count; i++ { + let level = levels[i] + for currentScope in level { + if scope == currentScope { + return i + 1 + } + } + } + if scope == baseScope { + return 0 + } + return -1 + } + func removeScopesInRange(range: NSRange) { - assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + assert(NSIntersectionRange(range, baseScope.range).length == range.length) for var level = levels.count-1; level >= 0; level-- { for var scope = levels[level].count-1; scope >= 0; scope-- { let theScope = levels[level][scope] - if NSIntersectionRange(theScope.range.entireRange, range).length == theScope.range.entireRange.length { + if NSIntersectionRange(theScope.range, range).length == theScope.range.length { levels[level].removeAtIndex(scope) } } @@ -205,29 +193,22 @@ class ScopedString: NSObject, NSCopying { } func insertString(string: String, atIndex index: Int) { - assert(index >= 0 && index <= baseScope.range.entireRange.length) + assert(index >= 0 && index <= baseScope.range.length) let s = underlyingString as NSString let length = (string as NSString).length - let indexRange = NSRange(location: index, length: 1) let mutableString = s.mutableCopy() as! NSMutableString mutableString.insertString(string, atIndex: index) self.underlyingString = mutableString.copy() as! String for level in 0.. index { - newScope.range.location += length - } - levels[level][scope] = newScope + levels[level][scope].range.insertRange(NSRange(location: index, length: length)) } } } func deleteCharactersInRange(range: NSRange) { - assert(NSIntersectionRange(range, baseScope.range.entireRange).length == range.length) + assert(NSIntersectionRange(range, baseScope.range).length == range.length) let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString mutableString.deleteCharactersInRange(range) @@ -253,7 +234,7 @@ class ScopedString: NSObject, NSCopying { private func findScopeIntersectionWithRange(range: NSRange, atLevel level: [Scope]) -> Scope? { for scope in level { - if NSIntersectionRange(scope.range.entireRange, range).length != 0 { + if NSIntersectionRange(scope.range, range).length != 0 { return scope } } @@ -263,7 +244,7 @@ class ScopedString: NSObject, NSCopying { private func insertionPointForRange(range: NSRange, atLevel level: [Scope]) -> Int { var i = 0 for scope in level { - if range.location < scope.range.entireRange.location { + if range.location < scope.range.location { return i } i++ diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 5dbe348..57c5be4 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -113,6 +113,6 @@ class IncrementalParsingTests: XCTestCase { XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) self.parser.parse(input, inRange: rangeToParse) { _, _ in return } - } + } } } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 6784fd2..2af63b4 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -25,26 +25,24 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.numberOfScopes() == 1) XCTAssert(newScopedString.numberOfLevels() == 1) - XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: true) == newScopedString.baseScope) + XCTAssert(newScopedString.topLevelScopeAtIndex(2) == newScopedString.baseScope) - let newScope1 = Scope(name: "bogus", range: HeadedRange(location: 1, headerLength: 1, bodyLength: 2), attribute: nil) - newScopedString.addScopeAtTopWithName(newScope1.name, inRange: newScope1.range) + let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) + newScopedString.addScopeAtTop(newScope1) XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) - XCTAssert(newScopedString.topLevelScopeAtIndex(0, onlyBodyResults: true) == newScopedString.baseScope) - XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: true) == newScopedString.baseScope) - XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false) == newScope1) + XCTAssert(newScopedString.topLevelScopeAtIndex(0) == newScopedString.baseScope) + XCTAssert(newScopedString.topLevelScopeAtIndex(1) == newScope1) XCTAssert(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1) == newScopedString.baseScope) - let newScope2 = Scope(name: "bogus2", range: HeadedRange(location: 2, headerLength: 0, bodyLength: 1), attribute: nil) - newScopedString.addScopeAtTopWithName(newScope2.name, inRange: newScope2.range) + let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) + newScopedString.addScopeAtTop(newScope2) XCTAssert(newScopedString.numberOfScopes() == 3) XCTAssert(newScopedString.numberOfLevels() == 3) - XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false) == newScope1) - XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: false) == newScope2) - XCTAssert(newScopedString.topLevelScopeAtIndex(3, onlyBodyResults: true) == newScope1) + XCTAssert(newScopedString.topLevelScopeAtIndex(1) == newScope1) + XCTAssert(newScopedString.topLevelScopeAtIndex(2) == newScope2) XCTAssertFalse(newScopedString.numberOfScopes() == 1) @@ -53,12 +51,53 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) - XCTAssert(newScopedString.topLevelScopeAtIndex(1, onlyBodyResults: false).range == HeadedRange(location: 1, headerLength: 1, bodyLength: 1)) + XCTAssert(newScopedString.topLevelScopeAtIndex(1).range == NSRange(location: 1, length: 2)) newScopedString.insertString("ssssss", atIndex: 2) XCTAssert(newScopedString.underlyingString == "Tesssssst") XCTAssert(newScopedString.numberOfScopes() == 2) - XCTAssert(newScopedString.topLevelScopeAtIndex(2, onlyBodyResults: false).range == HeadedRange(location: 1, headerLength: 1, bodyLength: 7)) + XCTAssert(newScopedString.topLevelScopeAtIndex(2).range == NSRange(location: 1, length: 8)) + } + + func testRangeExtension() { + var someRange = NSRange(location: 0, length: 24) + XCTAssertFalse(someRange.isEmpty()) + + someRange = NSRange(location: 49, length: 0) + XCTAssertTrue(someRange.isEmpty()) + + someRange = NSRange(location: 4, length: 2) + XCTAssertTrue(someRange.containsIndex(4)) + XCTAssertFalse(someRange.containsIndex(1)) + XCTAssertFalse(someRange.containsIndex(23)) + + someRange = NSRange(location: 0, length: 24) + someRange.subtractRange(NSRange(location: 2, length: 4)) + XCTAssertEqual(someRange, NSRange(location: 0, length: 20)) + + someRange = NSRange(location: 20, length: 40) + someRange.subtractRange(NSRange(location: 4, length: 12)) + XCTAssertEqual(someRange, NSRange(location: 8, length: 40)) + + someRange = NSRange(location: 23, length: 11) + someRange.subtractRange(NSRange(location: 20, length: 5)) + XCTAssertEqual(someRange, NSRange(location: 20, length: 9)) + + someRange = NSRange(location: 10, length: 14) + someRange.subtractRange(NSRange(location: 5, length: 40)) + XCTAssertTrue(someRange.isEmpty()) + + someRange = NSRange(location: 23, length: 11) + someRange.insertRange(NSRange(location: 20, length: 5)) + XCTAssertEqual(someRange, NSRange(location: 28, length: 11)) + + someRange = NSRange(location: 14, length: 2) + someRange.insertRange(NSRange(location: 15, length: 7)) + XCTAssertEqual(someRange, NSRange(location: 14, length: 9)) + + someRange = NSRange(location: 26, length: 36) + someRange.insertRange(NSRange(location: 62, length: 5)) + XCTAssertEqual(someRange, NSRange(location: 26, length: 36)) } } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 3ebc57a..c13000e 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -63,9 +63,9 @@ public struct Theme { // TODO: caret, invisibles, lightHighlight, selection, font style - if let scopes = raw["scope"] as? String { - for scope in scopes.componentsSeparatedByString(",") { - let key = scope.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + if let patternIdentifiers = raw["scope"] as? String { + for patternIdentifier in patternIdentifiers.componentsSeparatedByString(",") { + let key = patternIdentifier.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) attributes[key] = setting } } else if !setting.isEmpty { From 833d8f5400c3a257b82e021bb92e0d56a3131342 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 12 Feb 2016 15:05:35 +0100 Subject: [PATCH 025/110] adjust whitespace, add comments and encapsulation corrections --- SyntaxKit.xcodeproj/project.pbxproj | 8 - SyntaxKit/AttributedParser.swift | 36 ++-- SyntaxKit/Capture.swift | 10 +- SyntaxKit/CaptureCollection.swift | 20 +-- SyntaxKit/Include.swift | 38 ---- SyntaxKit/Language.swift | 16 +- SyntaxKit/Parser.swift | 102 ++++++----- SyntaxKit/Pattern.swift | 168 +++++++++++------- SyntaxKit/Patterns.swift | 15 +- SyntaxKit/Repository.swift | 13 +- SyntaxKit/Result.swift | 13 +- SyntaxKit/ResultSet.swift | 33 ++-- SyntaxKit/Scope.swift | 2 +- SyntaxKit/ScopedString.swift | 12 +- SyntaxKit/Tests/AttributedParserTests.swift | 12 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 8 +- SyntaxKit/Tests/LanguageTests.swift | 14 +- SyntaxKit/Tests/ParserTests.swift | 28 +-- SyntaxKit/Tests/ScopedStringTests.swift | 4 +- .../SwiftBaselineHighlightingTests.swift | 6 +- SyntaxKit/Tests/TestHelper.swift | 6 +- SyntaxKit/Tests/ThemeTests.swift | 10 +- SyntaxKit/Theme.swift | 8 +- 23 files changed, 302 insertions(+), 280 deletions(-) delete mode 100644 SyntaxKit/Include.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 11f9651..d7ce8d9 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -81,9 +81,6 @@ 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; - 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; - 8CEEC0E71C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; - 8CEEC0E81C4120A900BF3E85 /* Include.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E51C4120A900BF3E85 /* Include.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -164,7 +161,6 @@ 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patterns.swift; sourceTree = ""; }; 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; - 8CEEC0E51C4120A900BF3E85 /* Include.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Include.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -286,7 +282,6 @@ 211989901B2EC38B00F0D786 /* Language.swift */, 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, - 8CEEC0E51C4120A900BF3E85 /* Include.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, @@ -535,7 +530,6 @@ 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, - 8CEEC0E71C4120A900BF3E85 /* Include.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, 211989C81B2EC40500F0D786 /* Theme.swift in Sources */, 211989C61B2EC40500F0D786 /* Result.swift in Sources */, @@ -572,7 +566,6 @@ 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, 8C6C24C81C6BD66A0087E156 /* Scope.swift in Sources */, - 8CEEC0E61C4120A900BF3E85 /* Include.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, @@ -609,7 +602,6 @@ 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, - 8CEEC0E81C4120A900BF3E85 /* Include.swift in Sources */, 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */, 2198CED91B36D5DE00BD463F /* Result.swift in Sources */, 2198CED21B36D5DE00BD463F /* AttributedParser.swift in Sources */, diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 6eba32f..7aa1d59 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -7,33 +7,33 @@ // public class AttributedParser: Parser { - + // MARK: - Types - + public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void - - + + // MARK: - Properties - + public let theme: Theme - - + + // MARK: - Initializers - + public required init(language: Language, theme: Theme) { self.theme = theme super.init(language: language) } - - + + // MARK: - Parsing - + public func parse(string: String, inRange bounds: NSRange? = nil, match callback: AttributedCallback) { parse(string, inRange: bounds) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } - + public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { let output = NSMutableAttributedString(string: string, attributes: baseAttributes) output.beginEditing() @@ -45,17 +45,17 @@ public class AttributedParser: Parser { output.endEditing() return output } - - + + // MARK: - Private - + private func attributesForScope(scope: String) -> Attributes? { let components = scope.componentsSeparatedByString(".") as NSArray let count = components.count if count == 0 { return nil } - + var attributes = Attributes() for i in 0.. Capture? { return captures[index] } diff --git a/SyntaxKit/Include.swift b/SyntaxKit/Include.swift deleted file mode 100644 index 622ac4c..0000000 --- a/SyntaxKit/Include.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Include.swift -// SyntaxKit -// -// TODO: add support for more sophisticiated includes -// -// Created by Alexander Hedges on 09/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. -// - -import Foundation - -class Include: Pattern { - - private let referenceName: String - private var associatedRepository: Repository? - - init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { - self.referenceName = reference - self.associatedRepository = repository - super.init() - self.parent = parent - } - - func resolveReferenceWithRepository(repository: Repository, inLanguage language: Language) { - if referenceName.hasPrefix("#") { - let key = referenceName.substringFromIndex(referenceName.startIndex.successor()) - if let pattern = (associatedRepository ?? repository)[key] { - self.replaceWithPattern(pattern) - } - } else if referenceName == "$self" { - self.parent!.patterns = language.patterns - } else { - // TODO: import from other language - } - self.associatedRepository = nil - } -} diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 78016d9..be70afb 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -9,29 +9,28 @@ import Foundation public struct Language { - + // MARK: - Properties - + public let UUID: String public let name: String public let scopeName: String - var patterns: [Pattern] - + let patterns: [Pattern] + static let globalScope = "GLOBAL" - + // MARK: - Initializers - + public init?(dictionary: [NSObject: AnyObject]) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, scopeName = dictionary["scopeName"] as? String, array = dictionary["patterns"] as? [[NSObject: AnyObject]] else { return nil } - + self.UUID = UUID self.name = name self.scopeName = scopeName - Patterns.reset() self.patterns = Patterns.patternsForArray(array, inRepository: nil, caller: nil) let repository: Repository @@ -42,6 +41,5 @@ public struct Language { } Patterns.resolveReferencesWithRepository(repository, inLanguage: self) - Patterns.reset() } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 0b9d136..81decc4 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -7,27 +7,28 @@ // Turns out TextMate doesn't highlight things it should highlight according to // the grammar so this is not entirely straight forward. // -// It supports incremental parsing. The recommmend use case is to ask the class +// It supports incremental parsing. The recommmend usage is to ask the class // for the range it should be reparsed on the given change. This range can then // be passed to parsed. // // Created by Sam Soffes on 9/19/14. Edited by Alexander Hedges -// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// Copyright © 2014-2015 Sam Soffes. Copyright (C) 2016 Alexander Hedges. +// All rights reserved. // import Foundation public class Parser { - + // MARK: - Types - + public typealias Callback = (scope: String, range: NSRange) -> Void - - + + // MARK: - Properties - + public let language: Language - + // Contains the string previously passed to parse() if parse has already // been called. It stores all the associated scopes with hierarchical // information and ranges. The attributes of the scopes are values of type @@ -48,26 +49,26 @@ public class Parser { // MARK: - Initializers - + public init(language: Language) { self.language = language } - - + + // MARK: - Public // Algorithmic notes: // If change occurred in a block reparse the lines in which the change // happened and the range of the block from this point on. If the change // occurred in the global scope just reparse the lines that changed. - + /// Returns the range in the given string that should be re-parsed after the /// given change. /// /// This method returns a range that can be safely passed into parse so that /// only a part of the string has to be reparsed. - /// In fact passing anything other than this range or nil to parse might - /// lead to uninteded results but is not prohibited. + /// In fact passing anything other than this range to parse might lead to + /// uninteded results but is not prohibited. /// This method is only guaranteed to possibly not return nil if parse was /// called on the old string before this call. The only kinds of changed /// supported are single insertions and deletions of strings. @@ -84,13 +85,13 @@ public class Parser { /// - returns: A range in newString that can be safely re-parsed. Or nil if /// everything has to be reparsed. public func outdatedRangeForChangeInString(newString: String, changeIsInsertion insertion: Bool, changedRange range: NSRange) -> NSRange? { - let s = newString as NSString if !stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range) { return nil } let potentialNewString = scopesString!.copy() as! ScopedString + let s = newString as NSString let linesRange: NSRange if insertion { potentialNewString.insertString(s.substringWithRange(range), atIndex: range.location) @@ -104,7 +105,7 @@ public class Parser { if potentialNewString.underlyingString != newString { return nil } - + let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRange) - 1) if scopeAtIndex == potentialNewString.baseScope { return linesRange @@ -117,20 +118,21 @@ public class Parser { // Implementation notes: // The first part tries to find the context in which parsing should take // place (which block we are in), if any. - // The second part parses the string the until the full range is consumed. + // The second part parses the string the until the full range is consumed + // and it may exceed that range if further parts of the string are outdated - /// Parses the given string in the given range and calls the callback on + /// Parses the given string in the given range and calls the callback on /// every match of a scope /// /// If a range is treated more of a recommendation than a requirement. /// For best results supply a range that was returned from - /// outdatedRangeForChangeInString called before the call to this method. + /// outdatedRangeForChangeInString called before calling this method. /// /// - parameter string: The string that is parsed /// - parameter range: The range in which the string should be parsed. /// On nil the entire string will be parsed. /// - parameter callback: The callback to call on every match of a - /// language scope + /// pattern identifier of the language public func parse(string: String, var inRange bounds: NSRange? = nil, match callback: Callback) { var endScope: Scope? = nil if bounds == nil { @@ -179,7 +181,8 @@ public class Parser { if startIndex > endIndex && scopesString!.isInString(startIndex + 1) { let scopeAtIndex = scopesString!.topLevelScopeAtIndex(startIndex + 1) - if endScope == nil && scopesString!.levelForScope(scopeAtIndex) > 0 || endScope != nil && scopesString!.levelForScope(scopeAtIndex) > scopesString!.levelForScope(endScope!) { + if endScope == nil && scopesString!.levelForScope(scopeAtIndex) > 0 || + endScope != nil && scopesString!.levelForScope(scopeAtIndex) > scopesString!.levelForScope(endScope!) { endIndex = NSMaxRange(scopeAtIndex.range) } } @@ -240,7 +243,7 @@ public class Parser { } // MARK: Parsing - + // Algorithmic notes: // A pattern expression can not match a substring spanning multiple lines // so in the outer loop the string is decomposed into its lines. @@ -249,15 +252,15 @@ public class Parser { // This procedure is repeated with the subsequent lines until it has either // matched the end pattern or the string is consumed entirely. // If it can find neither in a line it moves to the next one. - + // Implementation note: // The matching of the middle part may return a match that goes beyond the // given range. This is intentional. - + /// Matches an array of patterns in the input /// - /// - parameter patterns: The patterns to match the sting against - /// - parameter string: The matched string + /// - parameter patterns: The patterns that should be matched + /// - parameter string: The string that should be matched against /// - parameter endPattern: If specified, the pattern at which to stop /// the matching process, overrides stopIndex. /// On nil it will match up to stopIndex. @@ -274,11 +277,11 @@ public class Parser { var lineStart = startIndex var lineEnd = startIndex var result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) - + while lineEnd < stop { s.getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) var range = NSRange(location: lineStart, length: lineEnd - lineStart) - + while range.length > 0 { let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) @@ -289,7 +292,7 @@ public class Parser { return result } } - + if bestResultForMiddle != nil && bestResultForMiddle!.range.length != 0 { result.addResults(bestResultForMiddle!) let newStart = NSMaxRange(bestResultForMiddle!.range) @@ -302,11 +305,11 @@ public class Parser { lineStart = lineEnd++ } - + result.extendWithRange(NSRange(location: startIndex, length: stop - startIndex)) return result } - + /// Helper method that iterates over the given patterns and tries to match /// them in order. /// @@ -323,11 +326,11 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange range: NSRange) -> ResultSet? { + private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { var bestResultForMiddle: ResultSet? for pattern in patterns { - let currRes = self.matchPattern(pattern, inString: string, inRange: range) - if currRes?.range.location == range.location { + let currRes = self.matchPattern(pattern, inString: string, inRange: bounds) + if currRes?.range.location == bounds.location { return currRes } else if bestResultForMiddle == nil || currRes != nil && currRes!.range.location < bestResultForMiddle!.range.location { bestResultForMiddle = currRes @@ -335,11 +338,11 @@ public class Parser { } return bestResultForMiddle } - + // Implementation note: // The order in which the beginning middle and end are added to the final // result matters. - + /// Matches a single pattern in the string in the given range /// /// A pattern may be one three options: @@ -361,7 +364,7 @@ public class Parser { guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { return nil } - + let newLocation = NSMaxRange(beginResults.range) let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) var result = ResultSet(startingRange: endResults.range) @@ -381,15 +384,15 @@ public class Parser { } return nil } - + /// Matches a given regular expression in a String and returns range /// information for the captures /// - /// - parameter expression: The regular expression to match - /// - parameter string: The string to match against - /// - parameter range: The range to which to restrict the match - /// - parameter captures: A collection of captures that can be used to add - /// extra information to parts of the match. + /// - parameter expression: The regular expression to match + /// - parameter string: The string to match against + /// - parameter range: The range to which to restrict the match + /// - parameter captures: A collection of captures that can be used to + /// add extra information to parts of the match. /// - parameter baseSelector: String to associate with the entire range of /// the match /// @@ -400,12 +403,12 @@ public class Parser { guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { return nil } - + var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) } - + if let captures = captures { for index in captures.captureIndexes { if result.numberOfRanges <= Int(index) { @@ -416,16 +419,21 @@ public class Parser { if range.location == NSNotFound { continue } - + if let scope = captures[index]?.name { resultSet.addResult(Result(identifier: scope, range: range)) } } } - + return resultSet } + /// Uses the callback to communicate the result of the parsing pass back + /// to the caller of parse. + /// + /// - parameter resultSet: The results of the parsing pass + /// - parameter callback: The method to call on every successful match private func applyResults(resultSet: ResultSet?, callback: Callback) { guard let results = resultSet else { return diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 48fb2a9..3bd7273 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -2,6 +2,12 @@ // Pattern.swift // SyntaxKit // +// Represents a pattern from a TextMate Language Bundle +// +// The Include class represents a Pattern that is a reference to another part +// of the Bundle. It is only fully functional as a pattern after it has been +// resolved via the provided method. +// // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -9,82 +15,77 @@ import Foundation class Pattern { - + // MARK: - Properties - - var name: String? - var match: NSRegularExpression? - var captures: CaptureCollection? - var begin: NSRegularExpression? - var beginCaptures: CaptureCollection? - var end: NSRegularExpression? - var endCaptures: CaptureCollection? - var parent: Pattern? - var patterns: [Pattern] - - var subpatterns: [Pattern] { - return patterns - } - // MARK: - Initializers + var name: String? { return _name } + var match: NSRegularExpression? { return _match } + var captures: CaptureCollection? { return _captures } + var begin: NSRegularExpression? { return _begin } + var beginCaptures: CaptureCollection? { return _beginCaptures } + var end: NSRegularExpression? { return _end } + var endCaptures: CaptureCollection? { return _endCaptures } + var parent: Pattern? { return _parent } + var subpatterns: [Pattern] { return _subpatterns } - init() { - self.name = nil - self.match = nil - self.captures = nil - self.begin = nil - self.beginCaptures = nil - self.end = nil - self.endCaptures = nil - self.patterns = [] - - } + private var _name: String? + private var _match: NSRegularExpression? + private var _captures: CaptureCollection? + private var _begin: NSRegularExpression? + private var _beginCaptures: CaptureCollection? + private var _end: NSRegularExpression? + private var _endCaptures: CaptureCollection? + private var _parent: Pattern? + private var _subpatterns: [Pattern] + + + // MARK: - Initializers init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?) { - self.parent = parent - self.name = dictionary["name"] as? String + _parent = parent + _name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) //[.CaseInsensitive] + _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) } else { - self.match = nil + _match = nil } if let beginExpr = dictionary["begin"] as? String { - self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) } else { - self.begin = nil + _begin = nil } if let endExpr = dictionary["end"] as? String { - self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) } else { - self.end = nil + _end = nil } if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { - self.beginCaptures = CaptureCollection(dictionary: dictionary) + _beginCaptures = CaptureCollection(dictionary: dictionary) } else { - self.beginCaptures = nil + _beginCaptures = nil } if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { - self.captures = CaptureCollection(dictionary: dictionary) + _captures = CaptureCollection(dictionary: dictionary) } else { - self.captures = nil + _captures = nil } if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { - self.endCaptures = CaptureCollection(dictionary: dictionary) + _endCaptures = CaptureCollection(dictionary: dictionary) } else { - self.endCaptures = nil + _endCaptures = nil } if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - self.patterns = [] - self.patterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) + _subpatterns = [] + _subpatterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) } else { - self.patterns = [] + _subpatterns = [] } if dictionary["match"] as? String != nil && self.match == nil { @@ -93,29 +94,72 @@ class Pattern { return nil } - if self.match == nil && self.begin == nil && self.end == nil && (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { + if self.match == nil && + self.begin == nil && + self.end == nil && + (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { return nil } } - - func replaceWithPattern(pattern: Pattern) { - self.name = pattern.name - self.match = pattern.match - self.captures = pattern.captures - self.begin = pattern.begin - self.beginCaptures = pattern.beginCaptures - self.end = pattern.end - self.endCaptures = pattern.endCaptures - self.patterns = pattern.patterns ?? [] + + private init() { + _name = nil + _match = nil + _captures = nil + _begin = nil + _beginCaptures = nil + _end = nil + _endCaptures = nil + _subpatterns = [] } } -func ==(lhs: Pattern, rhs: Pattern) -> Bool { - if lhs.name != rhs.name || - lhs.match != rhs.match || - lhs.begin != rhs.begin || - lhs.end != rhs.end { - return false + +class Include: Pattern { + + // MARK: - Properties + + private let referenceName: String + private var associatedRepository: Repository? + + + // MARK: - Initializers + + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { + self.referenceName = reference + self.associatedRepository = repository + super.init() + _parent = parent + } + + + // MARK: - Public + + func resolveReferenceWithRepository(repository: Repository, inLanguage language: Language) { + if referenceName.hasPrefix("#") { + let key = referenceName.substringFromIndex(referenceName.startIndex.successor()) + if let pattern = (associatedRepository ?? repository)[key] { + self.replaceWithPattern(pattern) + } + } else if referenceName == "$self" { + self.parent!._subpatterns = language.patterns + } else { + // TODO: import from other language + } + self.associatedRepository = nil + } + + + // MARK: - Private + + private func replaceWithPattern(pattern: Pattern) { + _name = pattern._name + _match = pattern.match + _captures = pattern.captures + _begin = pattern.begin + _beginCaptures = pattern.beginCaptures + _end = pattern.end + _endCaptures = pattern.endCaptures + _subpatterns = pattern._subpatterns ?? [] } - return true } diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift index 3e9238b..302e519 100644 --- a/SyntaxKit/Patterns.swift +++ b/SyntaxKit/Patterns.swift @@ -2,8 +2,16 @@ // Patterns.swift // SyntaxKit // +// A utility class to facilitate the creation of pattern arrays. +// It works it the following fashion: First all the pattern arrays should be +// created with patternsForArray:inRepository:caller:. Then +// resolveReferencesWithRepository:inLanguage: has to be called to resolve all +// the references in the passed out patterns. So first lots of calls to +// patternsForArray and then one call to resolveReferences to validate the +// patterns. +// // Created by Alexander Hedges on 09/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import Foundation @@ -12,10 +20,6 @@ class Patterns { private static var includes: [Include] = [] - class func reset() { - self.includes = [] - } - class func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { var results: [Pattern] = [] for rawPattern in patterns { @@ -34,5 +38,6 @@ class Patterns { for include in self.includes { include.resolveReferenceWithRepository(repository, inLanguage: language) } + self.includes = [] } } diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 42f437b..64a8e14 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -9,9 +9,15 @@ import Foundation class Repository { + + // MARK: - Properties + private var entries: [String: Pattern] private weak var parentRepository: Repository? - + + + // MARK: - Initializers + init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, inLanguage language: Language) { self.entries = [:] self.parentRepository = parent @@ -26,7 +32,10 @@ class Repository { } } } - + + + // MARK: - Accessing Patterns + subscript(index: String) -> Pattern? { if let resultAtLevel = entries[index] { return resultAtLevel diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index 11c5da8..0edc2c0 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -9,15 +9,15 @@ import Foundation class Result: Equatable { - + // MARK: - Properties - + let patternIdentifier: String var range: NSRange - - + + // MARK: - Initializers - + init(identifier: String, range: NSRange) { self.patternIdentifier = identifier self.range = range @@ -25,5 +25,6 @@ class Result: Equatable { } func ==(lhs: Result, rhs: Result) -> Bool { - return lhs.patternIdentifier == rhs.patternIdentifier && lhs.range.toRange() == rhs.range.toRange() + return lhs.patternIdentifier == rhs.patternIdentifier && + lhs.range.toRange() == rhs.range.toRange() } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 4f451f8..81e920b 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -9,41 +9,36 @@ import Foundation struct ResultSet { - + // MARK: - Properties - + + var results: [Result] { return _results } + var range: NSRange { return _range } + private var _results = [Result]() - var results: [Result] { - return _results - } - - var range: NSRange - - var isEmpty: Bool { - return results.isEmpty - } + private var _range: NSRange + - // MARK: - Initializers init(startingRange range: NSRange) { - self.range = range + _range = range } - + // MARK: - Adding mutating func extendWithRange(range: NSRange) { - self.range = NSUnionRange(self.range, range) + _range = NSUnionRange(self.range, range) } - + mutating func addResult(result: Result) { _results.append(result) - self.extendWithRange(result.range) + extendWithRange(result.range) } - + mutating func addResults(resultSet: ResultSet) { - self.extendWithRange(resultSet.range) + extendWithRange(resultSet.range) for result in resultSet.results { _results.append(result) } diff --git a/SyntaxKit/Scope.swift b/SyntaxKit/Scope.swift index 2e05ec7..1461d6d 100644 --- a/SyntaxKit/Scope.swift +++ b/SyntaxKit/Scope.swift @@ -11,7 +11,7 @@ import Foundation class Scope: Result { // MARK: - Properties - + let attribute: AnyObject? diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index b0d2362..f568b32 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -22,7 +22,7 @@ // the individual levels. // // Created by Alexander Hedges on 29/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import Foundation @@ -30,7 +30,7 @@ import Foundation extension NSRange { func isEmpty() -> Bool { - return length == 0 + return length == 0 } func containsIndex(index: Int) -> Bool { @@ -55,6 +55,8 @@ extension NSRange { class ScopedString: NSObject, NSCopying { + // MARK: - Properties + var underlyingString: String private var levels: [[Scope]] = [] @@ -63,10 +65,16 @@ class ScopedString: NSObject, NSCopying { return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (underlyingString as NSString).length), attribute: nil) } + + // MARK: - Initializers + init(string: String) { self.underlyingString = string } + + // MARK: - Interface + func copyWithZone(zone: NSZone) -> AnyObject { let newScopedString = ScopedString(string: self.underlyingString) newScopedString.levels = [] diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 469dc7e..86432d9 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -10,17 +10,17 @@ import XCTest import SyntaxKit class AttributedParserTests: XCTestCase { - + // MARK: - Properties - + let parser = AttributedParser(language: language("YAML"), theme: simpleTheme()) - - + + // MARK: - Tests - + func testParsing() { let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") - + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(0, effectiveRange: nil) as NSDictionary) XCTAssertEqual(["color": "red"] as NSDictionary, string.attributesAtIndex(7, effectiveRange: nil) as NSDictionary) XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(19, effectiveRange: nil) as NSDictionary) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 57c5be4..1dd92ab 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -12,7 +12,7 @@ import SyntaxKit class IncrementalParsingTests: XCTestCase { let parser = Parser(language: language("Swift")) - + override func setUp() { super.setUp() } @@ -20,7 +20,7 @@ class IncrementalParsingTests: XCTestCase { override func tearDown() { super.tearDown() } - + func testEdits() { let input = fixture("swifttest.swift", "txt") @@ -79,7 +79,7 @@ class IncrementalParsingTests: XCTestCase { parser.parse("") { _, _ in return } } - + func testPerformanceInScope() { let input = fixture("swifttest.swift", "txt") self.parser.parse(input) { _, _ in return } @@ -114,5 +114,5 @@ class IncrementalParsingTests: XCTestCase { self.parser.parse(input, inRange: rangeToParse) { _, _ in return } } - } + } } diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 6af0ed1..a148aa1 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -10,19 +10,19 @@ import XCTest @testable import SyntaxKit class LanguageTests: XCTestCase { - + // MARK: - Properties - + let yaml = language("YAML") - - + + // MARK: - Tests - + func testLoading() { XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) - + XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns[0].name) XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns[0].beginCaptures?[0]?.name) XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns[0].endCaptures?[0]?.name) @@ -31,7 +31,7 @@ class LanguageTests: XCTestCase { XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns[1].beginCaptures?[2]?.name) XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns[1].beginCaptures?[5]?.name) XCTAssertEqual("constant.numeric.yaml", yaml.patterns[2].name) - + let pattern = yaml.patterns[3] XCTAssertEqual("string.unquoted.yaml", pattern.name) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 05100fa..58b094d 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -10,55 +10,55 @@ import XCTest import SyntaxKit class ParserTests: XCTestCase { - + // MARK: - Properties - + let parser = Parser(language: language("YAML")) - - + + // MARK: - Tests - + func testParsingBeginEnd() { var stringQuoted: NSRange? var punctuationBegin: NSRange? var punctuationEnd: NSRange? - + parser.parse("title: \"Hello World\"\n") { scope, range in if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { stringQuoted = range } - + if punctuationBegin == nil && scope.hasPrefix("punctuation.definition.string.begin") { punctuationBegin = range } - + if punctuationEnd == nil && scope.hasPrefix("punctuation.definition.string.end") { punctuationEnd = range } } - + XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin) XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) } - + func testParsingBeginEndCrap() { var stringQuoted: NSRange? - + parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { scope, range in if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { stringQuoted = range } } - + XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) } - + func testParsingGarbage() { parser.parse("") { _, _ in } parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } } - + func testRuby() { let parser = Parser(language: language("Ruby")) let input = fixture("test.rb", "txt") diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 2af63b4..306bf71 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -11,7 +11,7 @@ import XCTest import SyntaxKit class ScopedStringTests: XCTestCase { - + override func setUp() { super.setUp() } @@ -19,7 +19,7 @@ class ScopedStringTests: XCTestCase { override func tearDown() { super.tearDown() } - + func testScopesString() { let newScopedString = ScopedString(string: "Test") XCTAssert(newScopedString.numberOfScopes() == 1) diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index e2c2c59..f47a9f0 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -13,7 +13,7 @@ import SyntaxKit class SwiftBaselineHighlightingTests: XCTestCase { let parser = AttributedParser(language: language("Swift"), theme: theme("Solarized")) - + override func setUp() { super.setUp() } @@ -21,7 +21,7 @@ class SwiftBaselineHighlightingTests: XCTestCase { override func tearDown() { super.tearDown() } - + func testColors() { let input = fixture("swifttest.swift", "txt") let string = parser.attributedStringForString(input) @@ -48,7 +48,7 @@ class SwiftBaselineHighlightingTests: XCTestCase { assertEqualColors(Color(hex: "#d33682"), string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } - + func testHighlightingPerformance() { let input = fixture("swifttest.swift", "txt") self.measureBlock { diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 7fcab24..2a4c787 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -68,19 +68,19 @@ extension Color { getRed(&value, green: nil, blue: nil, alpha: nil) return value } - + var greenComponent: CGFloat { var value: CGFloat = 0.0 getRed(nil, green: &value, blue: nil, alpha: nil) return value } - + var blueComponent: CGFloat { var value: CGFloat = 0.0 getRed(nil, green: nil, blue: &value, alpha: nil) return value } - + var alphaComponent: CGFloat { var value: CGFloat = 0.0 getRed(nil, green: nil, blue: nil, alpha: &value) diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 34fabd4..5426e30 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -10,21 +10,21 @@ import XCTest @testable import SyntaxKit class ThemeTests: XCTestCase { - + // MARK: - Properties - + let tomorrow = theme("Tomorrow") let solarized = theme("Solarized") - + // MARK: - Tests - + func testLoading() { XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) XCTAssertEqual("Tomorrow", tomorrow.name) assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) } - + func testComplexTheme() { XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) XCTAssertEqual("Solarized (light)", solarized.name) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index c13000e..c6888f7 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -15,9 +15,9 @@ import Foundation public typealias Attributes = [String: AnyObject] public struct Theme { - + // MARK: - Properties - + public let UUID: String public let name: String public let attributes: [String: Attributes] @@ -39,13 +39,13 @@ public struct Theme { } // MARK: - Initializers - + public init?(dictionary: [NSObject: AnyObject]) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, rawSettings = dictionary["settings"] as? [[String: AnyObject]] else { return nil } - + self.UUID = UUID self.name = name From 3d4baf6a7fda231c719e22da9eafff7f8bdffa5c Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 14 Feb 2016 17:20:32 +0100 Subject: [PATCH 026/110] fixes end pattern should always take precedence over body matches and include $self should not override the other patterns in the array. --- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Pattern.swift | 62 +++++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 81decc4..8d4695b 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -287,7 +287,7 @@ public class Parser { if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location < bestResultForMiddle!.range.location) { + if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location <= bestResultForMiddle!.range.location) { result.addResults(endMatchResult!) return result } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 3bd7273..d500f89 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -36,7 +36,9 @@ class Pattern { private var _end: NSRegularExpression? private var _endCaptures: CaptureCollection? private var _parent: Pattern? - private var _subpatterns: [Pattern] + private var _subpatterns: [Pattern] = [] + + private let debug = true // MARK: - Initializers @@ -47,45 +49,35 @@ class Pattern { if let matchExpr = dictionary["match"] as? String { _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) - } else { - _match = nil + if debug && _match == nil { + print("Problem parsing match expression \(matchExpr)") + } } if let beginExpr = dictionary["begin"] as? String { _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) - } else { - _begin = nil + if debug && _begin == nil { + print("Problem parsing begin expression \(beginExpr)") + } } if let endExpr = dictionary["end"] as? String { _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) - } else { - _end = nil + if debug && _end == nil { + print("Problem parsing end expression \(endExpr)") + } } if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { _beginCaptures = CaptureCollection(dictionary: dictionary) - } else { - _beginCaptures = nil } if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { _captures = CaptureCollection(dictionary: dictionary) - } else { - _captures = nil } if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { _endCaptures = CaptureCollection(dictionary: dictionary) - } else { - _endCaptures = nil - } - - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - _subpatterns = [] - _subpatterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) - } else { - _subpatterns = [] } if dictionary["match"] as? String != nil && self.match == nil { @@ -98,20 +90,16 @@ class Pattern { self.begin == nil && self.end == nil && (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { - return nil + print("Attention: pattern not recognized: \(self.name)") + return nil + } + + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + _subpatterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) } } - private init() { - _name = nil - _match = nil - _captures = nil - _begin = nil - _beginCaptures = nil - _end = nil - _endCaptures = nil - _subpatterns = [] - } + private init() {} } @@ -129,7 +117,7 @@ class Include: Pattern { self.referenceName = reference self.associatedRepository = repository super.init() - _parent = parent + self._parent = parent } @@ -142,7 +130,13 @@ class Include: Pattern { self.replaceWithPattern(pattern) } } else if referenceName == "$self" { - self.parent!._subpatterns = language.patterns + let position = self.parent!.subpatterns.indexOf { [unowned self] pattern in + if let include = pattern as? Include { + return include.referenceName == self.referenceName + } + return false + } + self.parent!._subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: language.patterns) } else { // TODO: import from other language } @@ -160,6 +154,6 @@ class Include: Pattern { _beginCaptures = pattern.beginCaptures _end = pattern.end _endCaptures = pattern.endCaptures - _subpatterns = pattern._subpatterns ?? [] + _subpatterns = pattern._subpatterns } } From a7d519dd5561505590f745a8eb8680d464983f8f Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 14 Feb 2016 23:48:17 +0100 Subject: [PATCH 027/110] fixes Mid patterns have precedence over end patterns and unhilighted begin/end parts should still be put into the scopes string --- SyntaxKit/Parser.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 8d4695b..e0d7b42 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -287,7 +287,7 @@ public class Parser { if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location <= bestResultForMiddle!.range.location) { + if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location < bestResultForMiddle!.range.location) { result.addResults(endMatchResult!) return result } @@ -440,10 +440,10 @@ public class Parser { } callback(scope: Language.globalScope, range: results.range) - for result in results.results where result.patternIdentifier != "" && result.range.length > 0 { + for result in results.results where result.range.length > 0 { if let scope = result as? Scope { self.scopesString?.addScopeAtBottom(scope) - } else { + } else if result.patternIdentifier != "" { callback(scope: result.patternIdentifier, range: result.range) } } From bb0fd238b88756fb7fd7eb73eae47f81f6df1f42 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 15 Feb 2016 00:51:47 +0100 Subject: [PATCH 028/110] decrease expectations --- SyntaxKit/Tests/IncrementalParsingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 1dd92ab..c4cf1e4 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -31,7 +31,7 @@ class IncrementalParsingTests: XCTestCase { var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 19)) + XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 136)) newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) From 7a962c7524a102211f1628e1de2772640f0c48f3 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 15 Feb 2016 16:43:04 +0100 Subject: [PATCH 029/110] remove assert, index may be -1 in certain conditions --- SyntaxKit/ScopedString.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index f568b32..fa67917 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -138,8 +138,6 @@ class ScopedString: NSObject, NSCopying { } func topLevelScopeAtIndex(index: Int) -> Scope { - assert(index >= 0 && index <= baseScope.range.length) - let indexRange = NSRange(location: index, length: 1) for var i = levels.count - 1; i >= 0; i-- { let level = levels[i] From b87633308a50a1a21632b6e383f0802b272c7817 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 18 Feb 2016 15:44:30 +0100 Subject: [PATCH 030/110] ridculously slow and stupid implementation --- SyntaxKit.xcodeproj/project.pbxproj | 52 +++-- SyntaxKit/BundleManager.swift | 77 ++++++++ SyntaxKit/Language.swift | 51 +++-- SyntaxKit/Parser.swift | 14 +- SyntaxKit/Pattern.swift | 148 +-------------- SyntaxKit/Patterns.swift | 43 ----- SyntaxKit/ProtoPattern.swift | 285 ++++++++++++++++++++++++++++ SyntaxKit/RefernceManager.swift | 97 ++++++++++ SyntaxKit/Repository.swift | 13 +- SyntaxKit/Tests/TestHelper.swift | 3 +- 10 files changed, 548 insertions(+), 235 deletions(-) create mode 100644 SyntaxKit/BundleManager.swift delete mode 100644 SyntaxKit/Patterns.swift create mode 100644 SyntaxKit/ProtoPattern.swift create mode 100644 SyntaxKit/RefernceManager.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index d7ce8d9..dcd140f 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 211989971B2EC38B00F0D786 /* AttributedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */; }; 211989981B2EC38B00F0D786 /* Capture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898D1B2EC38B00F0D786 /* Capture.swift */; }; 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */; }; - 2119899B1B2EC38B00F0D786 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989901B2EC38B00F0D786 /* Language.swift */; }; 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989911B2EC38B00F0D786 /* Parser.swift */; }; 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989921B2EC38B00F0D786 /* Pattern.swift */; }; 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989931B2EC38B00F0D786 /* Result.swift */; }; @@ -33,7 +32,6 @@ 211989BF1B2EC40500F0D786 /* AttributedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */; }; 211989C01B2EC40500F0D786 /* Capture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898D1B2EC38B00F0D786 /* Capture.swift */; }; 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */; }; - 211989C31B2EC40500F0D786 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989901B2EC38B00F0D786 /* Language.swift */; }; 211989C41B2EC40500F0D786 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989911B2EC38B00F0D786 /* Parser.swift */; }; 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989921B2EC38B00F0D786 /* Pattern.swift */; }; 211989C61B2EC40500F0D786 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989931B2EC38B00F0D786 /* Result.swift */; }; @@ -50,7 +48,6 @@ 2198CED21B36D5DE00BD463F /* AttributedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */; }; 2198CED31B36D5DE00BD463F /* Capture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898D1B2EC38B00F0D786 /* Capture.swift */; }; 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */; }; - 2198CED61B36D5DE00BD463F /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989901B2EC38B00F0D786 /* Language.swift */; }; 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989911B2EC38B00F0D786 /* Parser.swift */; }; 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989921B2EC38B00F0D786 /* Pattern.swift */; }; 2198CED91B36D5DE00BD463F /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989931B2EC38B00F0D786 /* Result.swift */; }; @@ -69,15 +66,24 @@ 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; + 8CDD6F1B1C71594B0063915A /* BundleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CDD6F1A1C71594B0063915A /* BundleManager.swift */; }; + 8CDD6F1C1C71594B0063915A /* BundleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CDD6F1A1C71594B0063915A /* BundleManager.swift */; }; + 8CDD6F1D1C71594B0063915A /* BundleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CDD6F1A1C71594B0063915A /* BundleManager.swift */; }; + 8CE64FE31C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; + 8CE64FE41C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; + 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; + 8CE64FE71C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; + 8CE64FE81C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; + 8CE64FE91C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; 8CE6BE2E1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; 8CE6BE2F1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; - 8CEEC0DE1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; - 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; - 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */; }; + 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; + 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; + 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; @@ -138,8 +144,7 @@ 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributedParser.swift; sourceTree = ""; }; 2119898D1B2EC38B00F0D786 /* Capture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Capture.swift; sourceTree = ""; }; 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaptureCollection.swift; sourceTree = ""; }; - 211989901B2EC38B00F0D786 /* Language.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; }; - 211989911B2EC38B00F0D786 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; + 211989911B2EC38B00F0D786 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Parser.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 211989921B2EC38B00F0D786 /* Pattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pattern.swift; sourceTree = ""; }; 211989931B2EC38B00F0D786 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 211989941B2EC38B00F0D786 /* ResultSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultSet.swift; sourceTree = ""; }; @@ -156,11 +161,14 @@ 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Solarized.tmTheme; sourceTree = ""; }; + 8CDD6F1A1C71594B0063915A /* BundleManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BundleManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8CE64FE21C74B48D0007BA57 /* Language.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Language.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ProtoPattern.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedStringTests.swift; sourceTree = ""; }; 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Swift.tmLanguage; sourceTree = ""; }; 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; - 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Patterns.swift; sourceTree = ""; }; - 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; }; + 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RefernceManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Repository.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -279,9 +287,11 @@ 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, - 211989901B2EC38B00F0D786 /* Language.swift */, - 8CEEC0DD1C411C8600BF3E85 /* Patterns.swift */, + 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, + 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, + 8CE64FE21C74B48D0007BA57 /* Language.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, + 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, @@ -522,17 +532,19 @@ buildActionMask = 2147483647; files = ( 211989C01B2EC40500F0D786 /* Capture.swift in Sources */, - 211989C31B2EC40500F0D786 /* Language.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, + 8CE64FE81C74BA720007BA57 /* ProtoPattern.swift in Sources */, 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */, 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, - 8CEEC0DF1C411C8600BF3E85 /* Patterns.swift in Sources */, + 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, + 8CDD6F1C1C71594B0063915A /* BundleManager.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, 211989C81B2EC40500F0D786 /* Theme.swift in Sources */, 211989C61B2EC40500F0D786 /* Result.swift in Sources */, + 8CE64FE41C74B48D0007BA57 /* Language.swift in Sources */, 211989BF1B2EC40500F0D786 /* AttributedParser.swift in Sources */, 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */, ); @@ -558,10 +570,10 @@ buildActionMask = 2147483647; files = ( 211989981B2EC38B00F0D786 /* Capture.swift in Sources */, - 2119899B1B2EC38B00F0D786 /* Language.swift in Sources */, + 8CDD6F1B1C71594B0063915A /* BundleManager.swift in Sources */, 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */, 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */, - 8CEEC0DE1C411C8600BF3E85 /* Patterns.swift in Sources */, + 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, @@ -570,6 +582,8 @@ 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989971B2EC38B00F0D786 /* AttributedParser.swift in Sources */, + 8CE64FE31C74B48D0007BA57 /* Language.swift in Sources */, + 8CE64FE71C74BA720007BA57 /* ProtoPattern.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -594,11 +608,13 @@ buildActionMask = 2147483647; files = ( 2198CED31B36D5DE00BD463F /* Capture.swift in Sources */, - 2198CED61B36D5DE00BD463F /* Language.swift in Sources */, + 8CDD6F1D1C71594B0063915A /* BundleManager.swift in Sources */, 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */, 8C6C24CA1C6BD66A0087E156 /* Scope.swift in Sources */, + 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, - 8CEEC0E01C411C8600BF3E85 /* Patterns.swift in Sources */, + 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, + 8CE64FE91C74BA720007BA57 /* ProtoPattern.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift new file mode 100644 index 0000000..c55419f --- /dev/null +++ b/SyntaxKit/BundleManager.swift @@ -0,0 +1,77 @@ +// +// BundleManager.swift +// SyntaxKit +// +// Created by Alexander Hedges on 15/02/16. +// Copyright © 2016 Alexander Hedges. All rights reserved. +// + +import UIKit + +public class BundleManager { + + public typealias BundleLocationCallback = (identifier: String, isLanguage: Bool) -> (NSURL?) + + private var bundleCallback: BundleLocationCallback + private var dependencies: [Language] = [] + private var cachedLanguages: [String: Language] = [:] + private var cachedProtoLanguages: [String: Language] = [:] + private var cachedThemes: [String: Theme] = [:] + + public static var defaultManager: BundleManager? + + public class func initializeGlobalManagerWithCallback(callback: BundleLocationCallback) { + defaultManager = BundleManager(callback: callback) + } + + init(callback: BundleLocationCallback) { + self.bundleCallback = callback + } + + public func getLanguageWithIdentifier(identifier: String) -> Language? { + if let language = self.cachedLanguages[identifier] { + return language + } + + self.dependencies = [] + let language = self.getProtoLanguageWithIdentifier(identifier)! + language.validateWithHelperLanguages(self.dependencies) + + self.cachedLanguages[identifier] = language + return language + } + + func getProtoLanguageWithIdentifier(identifier: String) -> Language? { + if let language = cachedProtoLanguages[identifier] { + if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { + self.dependencies.append(language) + } + return language + } + + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), + plist = NSDictionary(contentsOfURL: dictURL), + newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { + return nil + } + + self.dependencies.append(newLanguage) + cachedProtoLanguages[identifier] = newLanguage + return newLanguage + } + + public func getThemeWithIdentifier(identifier: String) -> Theme? { + if let theme = cachedThemes[identifier] { + return theme + } + + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), + plist = NSDictionary(contentsOfURL: dictURL), + newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { + return nil + } + + cachedThemes[identifier] = newTheme + return newTheme + } +} diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index be70afb..dda55b7 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -2,26 +2,27 @@ // Language.swift // SyntaxKit // -// Created by Sam Soffes on 9/18/14. -// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// Created by Alexander Hedges on 17/02/16. +// Copyright © 2016 Sam Soffes. All rights reserved. // import Foundation -public struct Language { +public class Language { - // MARK: - Properties + public var UUID: String = "" + public var name: String = "" + public var scopeName: String = "" + var referenceManager = ReferenceManager() - public let UUID: String - public let name: String - public let scopeName: String - let patterns: [Pattern] + var patterns: [ProtoPattern] = [] + var repository = Repository() static let globalScope = "GLOBAL" // MARK: - Initializers - public init?(dictionary: [NSObject: AnyObject]) { + init?(dictionary: [NSObject: AnyObject]) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, scopeName = dictionary["scopeName"] as? String, @@ -31,15 +32,29 @@ public struct Language { self.UUID = UUID self.name = name self.scopeName = scopeName - self.patterns = Patterns.patternsForArray(array, inRepository: nil, caller: nil) - - let repository: Repository - if let repo = dictionary["repository"] as? [String: [NSObject: AnyObject]] { - repository = Repository(repo: repo, inParent: nil, inLanguage: self) - } else { - repository = Repository(repo: [:], inParent: nil, inLanguage: self) + self.patterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) + self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, inLanguage: self, withReferenceManager: referenceManager) + referenceManager.resolveRepositoryReferences(repository) + referenceManager.resolveSelfReferences(self) + } + + func validateWithHelperLanguages(helperLanguages: [Language]) { + let resolvedProtoLanguage = Language.resolveReferencesBetweenThisAndProtoLanguages(self, andOtherLanguages: helperLanguages) + self.UUID = resolvedProtoLanguage.UUID + self.name = resolvedProtoLanguage.name + self.scopeName = resolvedProtoLanguage.scopeName + self.patterns = resolvedProtoLanguage.patterns + } + + private class func resolveReferencesBetweenThisAndProtoLanguages(thisLanguage: Language, andOtherLanguages otherLanguages: [Language]) -> Language { + let newLanguage = thisLanguage + var copyOfProtoLanguages: [Language] = [] + for language in otherLanguages { + let newOtherLang = ReferenceManager.copyLanguage(language) + copyOfProtoLanguages.append(newOtherLang) } - - Patterns.resolveReferencesWithRepository(repository, inLanguage: self) + ReferenceManager.resolveInterLanguageReferences(copyOfProtoLanguages, basename: thisLanguage.scopeName) + return newLanguage } + } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index e0d7b42..dad5fae 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -162,7 +162,7 @@ public class Parser { var allResults = ResultSet(startingRange: bounds!) while startIndex < endIndex { - let endPattern = endScope?.attribute as! Pattern? + let endPattern = endScope?.attribute as! ProtoPattern? let results = self.matchPatterns(endPattern?.subpatterns ?? language.patterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) if endScope != nil { @@ -269,7 +269,7 @@ public class Parser { /// /// - returns: The result set containing the lexical scope names with range /// information. May exceed stopIndex. - private func matchPatterns(patterns: [Pattern], withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { + private func matchPatterns(patterns: [ProtoPattern], withString string: String, withEndPatternFromPattern endPattern: ProtoPattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { assert(endPattern == nil || endPattern!.end != nil) let s: NSString = string @@ -326,7 +326,7 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { + private func findBestPatternInPatterns(patterns: [ProtoPattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { var bestResultForMiddle: ResultSet? for pattern in patterns { let currRes = self.matchPattern(pattern, inString: string, inRange: bounds) @@ -353,7 +353,7 @@ public class Parser { /// matched successfully /// /// - returns: The result of the match. Nil if unsuccessful - private func matchPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { + private func matchPattern(pattern: ProtoPattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { if let match = pattern.match { if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { @@ -404,6 +404,10 @@ public class Parser { return nil } + if (string as NSString).substringWithRange(bounds).containsString("@interface flickrCDTagsTableViewController ()") { +// print("string found") + } + var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) @@ -412,7 +416,7 @@ public class Parser { if let captures = captures { for index in captures.captureIndexes { if result.numberOfRanges <= Int(index) { - print("Attention unexpected expression: \(regularExpression.pattern)") + print("Attention unexpected capture (\(index) to \(result.numberOfRanges)): \(regularExpression.pattern)") continue } let range = result.rangeAtIndex(Int(index)) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index d500f89..b3e28ef 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -5,7 +5,7 @@ // Represents a pattern from a TextMate Language Bundle // // The Include class represents a Pattern that is a reference to another part -// of the Bundle. It is only fully functional as a pattern after it has been +// of the Bundle. It is only fully functional as a pattern after it has been // resolved via the provided method. // // Created by Sam Soffes on 9/18/14. @@ -14,146 +14,6 @@ import Foundation -class Pattern { - - // MARK: - Properties - - var name: String? { return _name } - var match: NSRegularExpression? { return _match } - var captures: CaptureCollection? { return _captures } - var begin: NSRegularExpression? { return _begin } - var beginCaptures: CaptureCollection? { return _beginCaptures } - var end: NSRegularExpression? { return _end } - var endCaptures: CaptureCollection? { return _endCaptures } - var parent: Pattern? { return _parent } - var subpatterns: [Pattern] { return _subpatterns } - - private var _name: String? - private var _match: NSRegularExpression? - private var _captures: CaptureCollection? - private var _begin: NSRegularExpression? - private var _beginCaptures: CaptureCollection? - private var _end: NSRegularExpression? - private var _endCaptures: CaptureCollection? - private var _parent: Pattern? - private var _subpatterns: [Pattern] = [] - - private let debug = true - - - // MARK: - Initializers - - init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?) { - _parent = parent - _name = dictionary["name"] as? String - - if let matchExpr = dictionary["match"] as? String { - _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) - if debug && _match == nil { - print("Problem parsing match expression \(matchExpr)") - } - } - - if let beginExpr = dictionary["begin"] as? String { - _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) - if debug && _begin == nil { - print("Problem parsing begin expression \(beginExpr)") - } - } - - if let endExpr = dictionary["end"] as? String { - _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) - if debug && _end == nil { - print("Problem parsing end expression \(endExpr)") - } - } - - if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { - _beginCaptures = CaptureCollection(dictionary: dictionary) - } - - if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { - _captures = CaptureCollection(dictionary: dictionary) - } - - if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { - _endCaptures = CaptureCollection(dictionary: dictionary) - } - - if dictionary["match"] as? String != nil && self.match == nil { - return nil - } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { - return nil - } - - if self.match == nil && - self.begin == nil && - self.end == nil && - (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { - print("Attention: pattern not recognized: \(self.name)") - return nil - } - - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - _subpatterns = Patterns.patternsForArray(array, inRepository: repository, caller: self) - } - } - - private init() {} -} - - -class Include: Pattern { - - // MARK: - Properties - - private let referenceName: String - private var associatedRepository: Repository? - - - // MARK: - Initializers - - init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { - self.referenceName = reference - self.associatedRepository = repository - super.init() - self._parent = parent - } - - - // MARK: - Public - - func resolveReferenceWithRepository(repository: Repository, inLanguage language: Language) { - if referenceName.hasPrefix("#") { - let key = referenceName.substringFromIndex(referenceName.startIndex.successor()) - if let pattern = (associatedRepository ?? repository)[key] { - self.replaceWithPattern(pattern) - } - } else if referenceName == "$self" { - let position = self.parent!.subpatterns.indexOf { [unowned self] pattern in - if let include = pattern as? Include { - return include.referenceName == self.referenceName - } - return false - } - self.parent!._subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: language.patterns) - } else { - // TODO: import from other language - } - self.associatedRepository = nil - } - - - // MARK: - Private - - private func replaceWithPattern(pattern: Pattern) { - _name = pattern._name - _match = pattern.match - _captures = pattern.captures - _begin = pattern.begin - _beginCaptures = pattern.beginCaptures - _end = pattern.end - _endCaptures = pattern.endCaptures - _subpatterns = pattern._subpatterns - } -} +//class Pattern { +// +//} diff --git a/SyntaxKit/Patterns.swift b/SyntaxKit/Patterns.swift deleted file mode 100644 index 302e519..0000000 --- a/SyntaxKit/Patterns.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Patterns.swift -// SyntaxKit -// -// A utility class to facilitate the creation of pattern arrays. -// It works it the following fashion: First all the pattern arrays should be -// created with patternsForArray:inRepository:caller:. Then -// resolveReferencesWithRepository:inLanguage: has to be called to resolve all -// the references in the passed out patterns. So first lots of calls to -// patternsForArray and then one call to resolveReferences to validate the -// patterns. -// -// Created by Alexander Hedges on 09/01/16. -// Copyright © 2016 Alexander Hedges. All rights reserved. -// - -import Foundation - -class Patterns { - - private static var includes: [Include] = [] - - class func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { - var results: [Pattern] = [] - for rawPattern in patterns { - if let include = rawPattern["include"] as? String { - let reference = Include(reference: include, inRepository: repository, parent: caller) - self.includes.append(reference) - results.append(reference) - } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, withRepository: repository) { - results.append(pattern) - } - } - return results - } - - class func resolveReferencesWithRepository(repository: Repository, inLanguage language: Language) { - for include in self.includes { - include.resolveReferenceWithRepository(repository, inLanguage: language) - } - self.includes = [] - } -} diff --git a/SyntaxKit/ProtoPattern.swift b/SyntaxKit/ProtoPattern.swift new file mode 100644 index 0000000..bca8393 --- /dev/null +++ b/SyntaxKit/ProtoPattern.swift @@ -0,0 +1,285 @@ +// +// ProtoPattern.swift +// SyntaxKit +// +// Created by Alexander Hedges on 17/02/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +import UIKit + +class ProtoPattern: NSObject { + + // MARK: - Properties + + var name: String? { return _name } + var match: NSRegularExpression? { return _match } + var captures: CaptureCollection? { return _captures } + var begin: NSRegularExpression? { return _begin } + var beginCaptures: CaptureCollection? { return _beginCaptures } + var end: NSRegularExpression? { return _end } + var endCaptures: CaptureCollection? { return _endCaptures } + weak var parent: ProtoPattern? + + private var _name: String? + private var _match: NSRegularExpression? + private var _captures: CaptureCollection? + private var _begin: NSRegularExpression? + private var _beginCaptures: CaptureCollection? + private var _end: NSRegularExpression? + private var _endCaptures: CaptureCollection? + var subpatterns: [ProtoPattern] = [] + + private let debug = true + + + // MARK: - Initializers + + init?(dictionary: [NSObject: AnyObject], parent: ProtoPattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { + super.init() + self.parent = parent + _name = dictionary["name"] as? String + + if let matchExpr = dictionary["match"] as? String { + _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) + if debug && _match == nil { + print("Problem parsing match expression \(matchExpr)") + } + } + + if let beginExpr = dictionary["begin"] as? String { + _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + if debug && _begin == nil { + print("Problem parsing begin expression \(beginExpr)") + } + } + + if let endExpr = dictionary["end"] as? String { + _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + if debug && _end == nil { + print("Problem parsing end expression \(endExpr)") + } + } + + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { + _beginCaptures = CaptureCollection(dictionary: dictionary) + } + + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { + if match != nil { + _captures = CaptureCollection(dictionary: dictionary) + } else if begin != nil && end != nil { + _beginCaptures = CaptureCollection(dictionary: dictionary) + _endCaptures = _beginCaptures + } + } + + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { + _endCaptures = CaptureCollection(dictionary: dictionary) + } + + if dictionary["match"] as? String != nil && self.match == nil { + return nil + } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + return nil + } + + if self.match == nil && + self.begin == nil && + self.end == nil && + (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { + print("Attention: pattern not recognized: \(self.name)") + return nil + } + + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) + } + } + + init(pattern: ProtoPattern, parent: ProtoPattern?) { + super.init() + _name = pattern.name + _match = pattern.match + _captures = pattern.captures + _begin = pattern.begin + _beginCaptures = pattern.beginCaptures + _end = pattern.end + _endCaptures = pattern.endCaptures + self.parent = parent + subpatterns = [] + } + + private override init() { + super.init() + } +} + +enum referenceType { + case toRepository + case toSelf + case toBase + case toForeign + case toForeignRepository + case resolved +} + +class Include: ProtoPattern { + + var type: referenceType + private let repositoryRef: String? + private let languageRef: String? + private let associatedRepository: Repository? + + init(reference: String, inRepository repository: Repository? = nil, parent: ProtoPattern?) { + self.associatedRepository = repository + if reference.hasPrefix("#") { + self.type = .toRepository + self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) + self.languageRef = nil + } else if reference == "$self" { + self.type = .toSelf + self.repositoryRef = nil + self.languageRef = nil + } else if reference == "$base" { + self.type = .toBase + self.repositoryRef = nil + self.languageRef = nil + } else if reference.containsString("#") { + self.type = .toForeignRepository + self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) + self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) + BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + } else { + self.type = .toForeign + self.repositoryRef = nil + self.languageRef = reference + BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + } + super.init() + self.parent = parent + } + + init(include: Include, parent: ProtoPattern?) { + self.type = include.type + self.repositoryRef = include.repositoryRef + self.languageRef = include.languageRef + self.associatedRepository = include.associatedRepository + super.init() + _name = include.name + _match = include.match + _captures = include.captures + _begin = include.begin + _beginCaptures = include.beginCaptures + _end = include.end + _endCaptures = include.endCaptures + self.parent = parent + subpatterns = [] + } + + func resolveRepositoryReferences(repository: Repository) { + if type == .toRepository { + if let pattern = (associatedRepository ?? repository)[repositoryRef!] { + self.replaceWithPattern(pattern) + } + self.type = .resolved + } + } + + func resolveSelfReferences(language: Language) { + if type == .toSelf { + let position = self.parent!.subpatterns.indexOf(isEqualToPattern) + if position != nil { + let newPatterns = flatCopyOfPatterns(language.patterns, inLanguage: language, newParent: self.parent!) + self.parent!.subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: newPatterns) + } + self.type = .resolved + } + } + + private func flatCopyOfPatterns(patterns: [ProtoPattern], inLanguage language: Language, newParent parent: ProtoPattern?) -> [ProtoPattern] { + var result: [ProtoPattern] = [] + for pattern in patterns { + let newPattern: ProtoPattern + if pattern as? Include != nil && (pattern as! Include).type != .resolved { + newPattern = Include(include: pattern as! Include, parent: parent) + language.referenceManager.includes.append(newPattern as! Include) + } else { + newPattern = ProtoPattern(pattern: pattern, parent: parent) + } + newPattern.subpatterns = pattern.subpatterns +// for subPattern in newPattern.subpatterns { +// subPattern.parent = newPattern +// } + result.append(newPattern) + } + return result + } + + func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { + if type == .toBase { + let position = self.parent!.subpatterns.indexOf(isEqualToPattern) + if position != nil { + let baseLanguage = languages[baseName!] + let newPatterns = flatCopyOfPatterns(baseLanguage!.patterns, inLanguage: ownLanguage, newParent: self.parent!) + self.parent!.subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: newPatterns) + } + self.type = .resolved + } else if type == .toForeignRepository { + let newLanguage = languages[languageRef!] + let pattern = newLanguage?.repository[repositoryRef!] + if self.parent != nil { + if let position = self.parent!.subpatterns.indexOf(isEqualToPattern) { + if pattern != nil { + self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: [pattern!]) + } else { + self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: []) + } + } + } else { + if let position = ownLanguage.patterns.indexOf(isEqualToPattern) { + if pattern != nil { + ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: [pattern!]) + } else { + ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: []) + } + } + } + self.type = .resolved + } else if type == .toForeign { + if let includedLang = languages[languageRef!] { + let newPatterns = flatCopyOfPatterns(includedLang.patterns, inLanguage: ownLanguage, newParent: self.parent) + if self.parent != nil { + if let position = self.parent!.subpatterns.indexOf(isEqualToPattern) { + self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: newPatterns) + } + } else { + if let position = ownLanguage.patterns.indexOf(isEqualToPattern) { + ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: newPatterns) + } + } + } + self.type = .resolved + } + } + + private func isEqualToPattern(pattern: ProtoPattern) -> Bool { + if let include = pattern as? Include { + return include.type == self.type && include.repositoryRef == self.repositoryRef && include.languageRef == self.languageRef + } + return false + } + + // MARK: - Private + + private func replaceWithPattern(pattern: ProtoPattern) { + _name = pattern._name + _match = pattern.match + _captures = pattern.captures + _begin = pattern.begin + _beginCaptures = pattern.beginCaptures + _end = pattern.end + _endCaptures = pattern.endCaptures + subpatterns = pattern.subpatterns + } +} diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift new file mode 100644 index 0000000..37dd6ac --- /dev/null +++ b/SyntaxKit/RefernceManager.swift @@ -0,0 +1,97 @@ +// +// Patterns.swift +// SyntaxKit +// +// A utility class to facilitate the creation of pattern arrays. +// It works it the following fashion: First all the pattern arrays should be +// created with patternsForArray:inRepository:caller:. Then +// resolveReferencesWithRepository:inLanguage: has to be called to resolve all +// the references in the passed out patterns. So first lots of calls to +// patternsForArray and then one call to resolveReferences to validate the +// patterns by resolving all references. +// +// Created by Alexander Hedges on 09/01/16. +// Copyright © 2016 Alexander Hedges. All rights reserved. +// + +import Foundation + +class ReferenceManager { + + var includes: [Include] = [] + + init() {} + + func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: ProtoPattern?) -> [ProtoPattern] { + var results: [ProtoPattern] = [] + for rawPattern in patterns { + if let include = rawPattern["include"] as? String { + let reference = Include(reference: include, inRepository: repository, parent: caller) + self.includes.append(reference) + results.append(reference) + } else if let pattern = ProtoPattern(dictionary: rawPattern, parent: caller, withRepository: repository, withReferenceManager: self) { + results.append(pattern) + } + } + return results + } + + func resolveRepositoryReferences(repository: Repository) { + for include in includes where include.type == .toRepository { + include.resolveRepositoryReferences(repository) + } + } + + func resolveSelfReferences(language: Language) { + for include in includes where include.type == .toSelf { + include.resolveSelfReferences(language) + } + } + + class func resolveInterLanguageReferences(languages: [Language], basename: String) { + var otherLanguages: [String: Language] = [:] + for language in languages { + otherLanguages[language.scopeName] = language + } + for language in languages { + while true { + let includes = language.referenceManager.includes + if includes.filter({ $0.type != .resolved }) == [] { + break + } + for include in includes where include.type == .toBase || include.type == .toForeign || include.type == .toForeignRepository { + include.resolveInterLanguageReferences(language, inLanguages: otherLanguages, baseName: basename) + } + } + } + } + + class func copyLanguage(language: Language) -> Language { + let newLanguage = language + newLanguage.referenceManager.includes = [] + newLanguage.patterns = ReferenceManager.copyPatternTree(language.patterns, inLanguage: newLanguage) + return newLanguage + } + + private class func copyPatternTree(patterns: [ProtoPattern], parent: ProtoPattern? = nil, foundPatterns: [ProtoPattern: ProtoPattern] = [:], inLanguage language: Language) -> [ProtoPattern] { + var newFoundPatterns = foundPatterns + var result: [ProtoPattern] = [] + for pattern in patterns { + if let visitedPattern = foundPatterns[pattern] { + result.append(visitedPattern) + } else { + let newPattern: ProtoPattern + if pattern as? Include != nil && (pattern as! Include).type != .resolved { + newPattern = Include(include: pattern as! Include, parent: parent) + language.referenceManager.includes.append(newPattern as! Include) + } else { + newPattern = ProtoPattern(pattern: pattern, parent: parent) + } + newFoundPatterns[pattern] = newPattern + newPattern.subpatterns = copyPatternTree(pattern.subpatterns, parent: newPattern, foundPatterns: newFoundPatterns, inLanguage: language) + result.append(newPattern) + } + } + return result + } +} diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 64a8e14..dfff216 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -12,22 +12,23 @@ class Repository { // MARK: - Properties - private var entries: [String: Pattern] + private var entries: [String: ProtoPattern] = [:] private weak var parentRepository: Repository? // MARK: - Initializers - init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, inLanguage language: Language) { - self.entries = [:] + init() {} + + init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, inLanguage language: Language, withReferenceManager refman: ReferenceManager) { self.parentRepository = parent for (key, value) in repo { var subRepo: Repository? if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { - subRepo = Repository(repo: containedRepo, inParent: self, inLanguage: language) + subRepo = Repository(repo: containedRepo, inParent: self, inLanguage: language, withReferenceManager: refman) } - if let pattern = Pattern(dictionary: value, parent: nil, withRepository: subRepo) { + if let pattern = ProtoPattern(dictionary: value, parent: nil, withRepository: subRepo, withReferenceManager: refman) { self.entries[key] = pattern } } @@ -36,7 +37,7 @@ class Repository { // MARK: - Accessing Patterns - subscript(index: String) -> Pattern? { + subscript(index: String) -> ProtoPattern? { if let resultAtLevel = entries[index] { return resultAtLevel } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 2a4c787..854fe2f 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -18,7 +18,8 @@ func fixture(name: String, _ type: String) -> String! { func language(name: String) -> Language! { let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - return Language(dictionary: plist)! + let language = Language(dictionary: plist)! + return language.validatedLanguage() } func theme(name: String) -> Theme! { From 3b46e500df5ed72af56334dac84b202463d9ea0a Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 18 Feb 2016 16:07:39 +0100 Subject: [PATCH 031/110] beautiful fix now the language bundle is finally parsed correctly! --- SyntaxKit.xcodeproj/project.pbxproj | 10 +- SyntaxKit/Language.swift | 6 +- SyntaxKit/Parser.swift | 10 +- SyntaxKit/Pattern.swift | 213 ++++++++++++++++++++- SyntaxKit/ProtoPattern.swift | 285 ---------------------------- SyntaxKit/RefernceManager.swift | 27 ++- SyntaxKit/Repository.swift | 6 +- 7 files changed, 233 insertions(+), 324 deletions(-) delete mode 100644 SyntaxKit/ProtoPattern.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index dcd140f..f5b76f9 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -72,9 +72,6 @@ 8CE64FE31C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; 8CE64FE41C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE21C74B48D0007BA57 /* Language.swift */; }; - 8CE64FE71C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; - 8CE64FE81C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; - 8CE64FE91C74BA720007BA57 /* ProtoPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */; }; 8CE6BE2E1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; 8CE6BE2F1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; @@ -145,7 +142,7 @@ 2119898D1B2EC38B00F0D786 /* Capture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Capture.swift; sourceTree = ""; }; 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaptureCollection.swift; sourceTree = ""; }; 211989911B2EC38B00F0D786 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Parser.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 211989921B2EC38B00F0D786 /* Pattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pattern.swift; sourceTree = ""; }; + 211989921B2EC38B00F0D786 /* Pattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Pattern.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 211989931B2EC38B00F0D786 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 211989941B2EC38B00F0D786 /* ResultSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultSet.swift; sourceTree = ""; }; 211989951B2EC38B00F0D786 /* SyntaxKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyntaxKit.h; sourceTree = ""; }; @@ -163,7 +160,6 @@ 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Solarized.tmTheme; sourceTree = ""; }; 8CDD6F1A1C71594B0063915A /* BundleManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BundleManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8CE64FE21C74B48D0007BA57 /* Language.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Language.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ProtoPattern.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedStringTests.swift; sourceTree = ""; }; 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Swift.tmLanguage; sourceTree = ""; }; 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; @@ -291,7 +287,6 @@ 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, 8CE64FE21C74B48D0007BA57 /* Language.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, - 8CE64FE61C74BA720007BA57 /* ProtoPattern.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, @@ -534,7 +529,6 @@ 211989C01B2EC40500F0D786 /* Capture.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, - 8CE64FE81C74BA720007BA57 /* ProtoPattern.swift in Sources */, 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */, 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, @@ -583,7 +577,6 @@ 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989971B2EC38B00F0D786 /* AttributedParser.swift in Sources */, 8CE64FE31C74B48D0007BA57 /* Language.swift in Sources */, - 8CE64FE71C74BA720007BA57 /* ProtoPattern.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -614,7 +607,6 @@ 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, - 8CE64FE91C74BA720007BA57 /* ProtoPattern.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index dda55b7..c6fc15d 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -15,7 +15,7 @@ public class Language { public var scopeName: String = "" var referenceManager = ReferenceManager() - var patterns: [ProtoPattern] = [] + var pattern: Pattern = Pattern() var repository = Repository() static let globalScope = "GLOBAL" @@ -32,7 +32,7 @@ public class Language { self.UUID = UUID self.name = name self.scopeName = scopeName - self.patterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) + self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, inLanguage: self, withReferenceManager: referenceManager) referenceManager.resolveRepositoryReferences(repository) referenceManager.resolveSelfReferences(self) @@ -43,7 +43,7 @@ public class Language { self.UUID = resolvedProtoLanguage.UUID self.name = resolvedProtoLanguage.name self.scopeName = resolvedProtoLanguage.scopeName - self.patterns = resolvedProtoLanguage.patterns + self.pattern = resolvedProtoLanguage.pattern } private class func resolveReferencesBetweenThisAndProtoLanguages(thisLanguage: Language, andOtherLanguages otherLanguages: [Language]) -> Language { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index dad5fae..379d48f 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -162,8 +162,8 @@ public class Parser { var allResults = ResultSet(startingRange: bounds!) while startIndex < endIndex { - let endPattern = endScope?.attribute as! ProtoPattern? - let results = self.matchPatterns(endPattern?.subpatterns ?? language.patterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) + let endPattern = endScope?.attribute as! Pattern? + let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) if endScope != nil { allResults.addResult(Result(identifier: endScope!.patternIdentifier, range: results.range)) @@ -269,7 +269,7 @@ public class Parser { /// /// - returns: The result set containing the lexical scope names with range /// information. May exceed stopIndex. - private func matchPatterns(patterns: [ProtoPattern], withString string: String, withEndPatternFromPattern endPattern: ProtoPattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { + private func matchPatterns(patterns: [Pattern], withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { assert(endPattern == nil || endPattern!.end != nil) let s: NSString = string @@ -326,7 +326,7 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [ProtoPattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { + private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { var bestResultForMiddle: ResultSet? for pattern in patterns { let currRes = self.matchPattern(pattern, inString: string, inRange: bounds) @@ -353,7 +353,7 @@ public class Parser { /// matched successfully /// /// - returns: The result of the match. Nil if unsuccessful - private func matchPattern(pattern: ProtoPattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { + private func matchPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { if let match = pattern.match { if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index b3e28ef..346c4ad 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -14,6 +14,213 @@ import Foundation -//class Pattern { -// -//} +class Pattern: NSObject { + + // MARK: - Properties + + var name: String? + var match: NSRegularExpression? + var captures: CaptureCollection? + var begin: NSRegularExpression? + var beginCaptures: CaptureCollection? + var end: NSRegularExpression? + var endCaptures: CaptureCollection? + weak var parent: Pattern? + var subpatterns: [Pattern] = [] + + private let debug = true + + + // MARK: - Initializers + + init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { + super.init() + self.parent = parent + self.name = dictionary["name"] as? String + + if let matchExpr = dictionary["match"] as? String { + self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) + if debug && self.match == nil { + print("Problem parsing match expression \(matchExpr)") + } + } + + if let beginExpr = dictionary["begin"] as? String { + self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + if debug && self.begin == nil { + print("Problem parsing begin expression \(beginExpr)") + } + } + + if let endExpr = dictionary["end"] as? String { + self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + if debug && self.end == nil { + print("Problem parsing end expression \(endExpr)") + } + } + + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { + self.beginCaptures = CaptureCollection(dictionary: dictionary) + } + + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { + if match != nil { + self.captures = CaptureCollection(dictionary: dictionary) + } else if begin != nil && end != nil { + self.beginCaptures = CaptureCollection(dictionary: dictionary) + self.endCaptures = self.beginCaptures + } + } + + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { + self.endCaptures = CaptureCollection(dictionary: dictionary) + } + + if dictionary["match"] as? String != nil && self.match == nil { + return nil + } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + return nil + } + + if self.match == nil && + self.begin == nil && + self.end == nil && + (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { + print("Attention: pattern not recognized: \(self.name)") + return nil + } + + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { + subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) + } + } + + init(pattern: Pattern, parent: Pattern?) { + super.init() + self.name = pattern.name + self.match = pattern.match + self.captures = pattern.captures + self.begin = pattern.begin + self.beginCaptures = pattern.beginCaptures + self.end = pattern.end + self.endCaptures = pattern.endCaptures + self.parent = parent + subpatterns = [] + } + + override init() { + super.init() + } +} + +enum referenceType { + case toRepository + case toSelf + case toBase + case toForeign + case toForeignRepository + case resolved +} + +class Include: Pattern { + + var type: referenceType + private let repositoryRef: String? + private let languageRef: String? + private let associatedRepository: Repository? + + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { + self.associatedRepository = repository + if reference.hasPrefix("#") { + self.type = .toRepository + self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) + self.languageRef = nil + } else if reference == "$self" { + self.type = .toSelf + self.repositoryRef = nil + self.languageRef = nil + } else if reference == "$base" { + self.type = .toBase + self.repositoryRef = nil + self.languageRef = nil + } else if reference.containsString("#") { + self.type = .toForeignRepository + self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) + self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) + BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + } else { + self.type = .toForeign + self.repositoryRef = nil + self.languageRef = reference + BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + } + super.init() + self.parent = parent + } + + init(include: Include, parent: Pattern?) { + self.type = include.type + self.repositoryRef = include.repositoryRef + self.languageRef = include.languageRef + self.associatedRepository = include.associatedRepository + super.init() + self.name = include.name + self.match = include.match + self.captures = include.captures + self.begin = include.begin + self.beginCaptures = include.beginCaptures + self.end = include.end + self.endCaptures = include.endCaptures + self.parent = parent + subpatterns = [] + } + + func resolveRepositoryReferences(repository: Repository) { + if type == .toRepository { + if let pattern = (associatedRepository ?? repository)[repositoryRef!] { + self.replaceWithPattern(pattern) + } + self.type = .resolved + } + } + + func resolveSelfReferences(language: Language) { + if type == .toSelf { + self.replaceWithPattern(language.pattern) + self.type = .resolved + } + } + + func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { + if type == .toBase { + let baseLanguage = languages[baseName!]! + self.replaceWithPattern(baseLanguage.pattern) + self.type = .resolved + } else if type == .toForeignRepository { + let newLanguage = languages[languageRef!] + if let pattern = newLanguage?.repository[repositoryRef!] { + self.replaceWithPattern(pattern) + } + self.type = .resolved + } else if type == .toForeign { + if let includedLang = languages[languageRef!] { + self.replaceWithPattern(includedLang.pattern) + } + self.type = .resolved + } + } + + // MARK: - Private + + private func replaceWithPattern(pattern: Pattern) { + self.name = pattern.name + self.match = pattern.match + self.captures = pattern.captures + self.begin = pattern.begin + self.beginCaptures = pattern.beginCaptures + self.end = pattern.end + self.endCaptures = pattern.endCaptures + subpatterns = pattern.subpatterns + } +} + diff --git a/SyntaxKit/ProtoPattern.swift b/SyntaxKit/ProtoPattern.swift deleted file mode 100644 index bca8393..0000000 --- a/SyntaxKit/ProtoPattern.swift +++ /dev/null @@ -1,285 +0,0 @@ -// -// ProtoPattern.swift -// SyntaxKit -// -// Created by Alexander Hedges on 17/02/16. -// Copyright © 2016 Sam Soffes. All rights reserved. -// - -import UIKit - -class ProtoPattern: NSObject { - - // MARK: - Properties - - var name: String? { return _name } - var match: NSRegularExpression? { return _match } - var captures: CaptureCollection? { return _captures } - var begin: NSRegularExpression? { return _begin } - var beginCaptures: CaptureCollection? { return _beginCaptures } - var end: NSRegularExpression? { return _end } - var endCaptures: CaptureCollection? { return _endCaptures } - weak var parent: ProtoPattern? - - private var _name: String? - private var _match: NSRegularExpression? - private var _captures: CaptureCollection? - private var _begin: NSRegularExpression? - private var _beginCaptures: CaptureCollection? - private var _end: NSRegularExpression? - private var _endCaptures: CaptureCollection? - var subpatterns: [ProtoPattern] = [] - - private let debug = true - - - // MARK: - Initializers - - init?(dictionary: [NSObject: AnyObject], parent: ProtoPattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { - super.init() - self.parent = parent - _name = dictionary["name"] as? String - - if let matchExpr = dictionary["match"] as? String { - _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) - if debug && _match == nil { - print("Problem parsing match expression \(matchExpr)") - } - } - - if let beginExpr = dictionary["begin"] as? String { - _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) - if debug && _begin == nil { - print("Problem parsing begin expression \(beginExpr)") - } - } - - if let endExpr = dictionary["end"] as? String { - _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) - if debug && _end == nil { - print("Problem parsing end expression \(endExpr)") - } - } - - if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { - _beginCaptures = CaptureCollection(dictionary: dictionary) - } - - if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { - if match != nil { - _captures = CaptureCollection(dictionary: dictionary) - } else if begin != nil && end != nil { - _beginCaptures = CaptureCollection(dictionary: dictionary) - _endCaptures = _beginCaptures - } - } - - if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { - _endCaptures = CaptureCollection(dictionary: dictionary) - } - - if dictionary["match"] as? String != nil && self.match == nil { - return nil - } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { - return nil - } - - if self.match == nil && - self.begin == nil && - self.end == nil && - (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { - print("Attention: pattern not recognized: \(self.name)") - return nil - } - - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) - } - } - - init(pattern: ProtoPattern, parent: ProtoPattern?) { - super.init() - _name = pattern.name - _match = pattern.match - _captures = pattern.captures - _begin = pattern.begin - _beginCaptures = pattern.beginCaptures - _end = pattern.end - _endCaptures = pattern.endCaptures - self.parent = parent - subpatterns = [] - } - - private override init() { - super.init() - } -} - -enum referenceType { - case toRepository - case toSelf - case toBase - case toForeign - case toForeignRepository - case resolved -} - -class Include: ProtoPattern { - - var type: referenceType - private let repositoryRef: String? - private let languageRef: String? - private let associatedRepository: Repository? - - init(reference: String, inRepository repository: Repository? = nil, parent: ProtoPattern?) { - self.associatedRepository = repository - if reference.hasPrefix("#") { - self.type = .toRepository - self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) - self.languageRef = nil - } else if reference == "$self" { - self.type = .toSelf - self.repositoryRef = nil - self.languageRef = nil - } else if reference == "$base" { - self.type = .toBase - self.repositoryRef = nil - self.languageRef = nil - } else if reference.containsString("#") { - self.type = .toForeignRepository - self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) - self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) - BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) - } else { - self.type = .toForeign - self.repositoryRef = nil - self.languageRef = reference - BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) - } - super.init() - self.parent = parent - } - - init(include: Include, parent: ProtoPattern?) { - self.type = include.type - self.repositoryRef = include.repositoryRef - self.languageRef = include.languageRef - self.associatedRepository = include.associatedRepository - super.init() - _name = include.name - _match = include.match - _captures = include.captures - _begin = include.begin - _beginCaptures = include.beginCaptures - _end = include.end - _endCaptures = include.endCaptures - self.parent = parent - subpatterns = [] - } - - func resolveRepositoryReferences(repository: Repository) { - if type == .toRepository { - if let pattern = (associatedRepository ?? repository)[repositoryRef!] { - self.replaceWithPattern(pattern) - } - self.type = .resolved - } - } - - func resolveSelfReferences(language: Language) { - if type == .toSelf { - let position = self.parent!.subpatterns.indexOf(isEqualToPattern) - if position != nil { - let newPatterns = flatCopyOfPatterns(language.patterns, inLanguage: language, newParent: self.parent!) - self.parent!.subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: newPatterns) - } - self.type = .resolved - } - } - - private func flatCopyOfPatterns(patterns: [ProtoPattern], inLanguage language: Language, newParent parent: ProtoPattern?) -> [ProtoPattern] { - var result: [ProtoPattern] = [] - for pattern in patterns { - let newPattern: ProtoPattern - if pattern as? Include != nil && (pattern as! Include).type != .resolved { - newPattern = Include(include: pattern as! Include, parent: parent) - language.referenceManager.includes.append(newPattern as! Include) - } else { - newPattern = ProtoPattern(pattern: pattern, parent: parent) - } - newPattern.subpatterns = pattern.subpatterns -// for subPattern in newPattern.subpatterns { -// subPattern.parent = newPattern -// } - result.append(newPattern) - } - return result - } - - func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { - if type == .toBase { - let position = self.parent!.subpatterns.indexOf(isEqualToPattern) - if position != nil { - let baseLanguage = languages[baseName!] - let newPatterns = flatCopyOfPatterns(baseLanguage!.patterns, inLanguage: ownLanguage, newParent: self.parent!) - self.parent!.subpatterns.replaceRange(NSRange(location: position!, length: 1).toRange()!, with: newPatterns) - } - self.type = .resolved - } else if type == .toForeignRepository { - let newLanguage = languages[languageRef!] - let pattern = newLanguage?.repository[repositoryRef!] - if self.parent != nil { - if let position = self.parent!.subpatterns.indexOf(isEqualToPattern) { - if pattern != nil { - self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: [pattern!]) - } else { - self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: []) - } - } - } else { - if let position = ownLanguage.patterns.indexOf(isEqualToPattern) { - if pattern != nil { - ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: [pattern!]) - } else { - ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: []) - } - } - } - self.type = .resolved - } else if type == .toForeign { - if let includedLang = languages[languageRef!] { - let newPatterns = flatCopyOfPatterns(includedLang.patterns, inLanguage: ownLanguage, newParent: self.parent) - if self.parent != nil { - if let position = self.parent!.subpatterns.indexOf(isEqualToPattern) { - self.parent!.subpatterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: newPatterns) - } - } else { - if let position = ownLanguage.patterns.indexOf(isEqualToPattern) { - ownLanguage.patterns.replaceRange(NSRange(location: position, length: 1).toRange()!, with: newPatterns) - } - } - } - self.type = .resolved - } - } - - private func isEqualToPattern(pattern: ProtoPattern) -> Bool { - if let include = pattern as? Include { - return include.type == self.type && include.repositoryRef == self.repositoryRef && include.languageRef == self.languageRef - } - return false - } - - // MARK: - Private - - private func replaceWithPattern(pattern: ProtoPattern) { - _name = pattern._name - _match = pattern.match - _captures = pattern.captures - _begin = pattern.begin - _beginCaptures = pattern.beginCaptures - _end = pattern.end - _endCaptures = pattern.endCaptures - subpatterns = pattern.subpatterns - } -} diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 37dd6ac..96ad7ba 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -22,14 +22,14 @@ class ReferenceManager { init() {} - func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: ProtoPattern?) -> [ProtoPattern] { - var results: [ProtoPattern] = [] + func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { + var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { let reference = Include(reference: include, inRepository: repository, parent: caller) self.includes.append(reference) results.append(reference) - } else if let pattern = ProtoPattern(dictionary: rawPattern, parent: caller, withRepository: repository, withReferenceManager: self) { + } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, withRepository: repository, withReferenceManager: self) { results.append(pattern) } } @@ -54,14 +54,9 @@ class ReferenceManager { otherLanguages[language.scopeName] = language } for language in languages { - while true { - let includes = language.referenceManager.includes - if includes.filter({ $0.type != .resolved }) == [] { - break - } - for include in includes where include.type == .toBase || include.type == .toForeign || include.type == .toForeignRepository { - include.resolveInterLanguageReferences(language, inLanguages: otherLanguages, baseName: basename) - } + let includes = language.referenceManager.includes + for include in includes where include.type == .toBase || include.type == .toForeign || include.type == .toForeignRepository { + include.resolveInterLanguageReferences(language, inLanguages: otherLanguages, baseName: basename) } } } @@ -69,23 +64,23 @@ class ReferenceManager { class func copyLanguage(language: Language) -> Language { let newLanguage = language newLanguage.referenceManager.includes = [] - newLanguage.patterns = ReferenceManager.copyPatternTree(language.patterns, inLanguage: newLanguage) + newLanguage.pattern.subpatterns = ReferenceManager.copyPatternTree(language.pattern.subpatterns, inLanguage: newLanguage) return newLanguage } - private class func copyPatternTree(patterns: [ProtoPattern], parent: ProtoPattern? = nil, foundPatterns: [ProtoPattern: ProtoPattern] = [:], inLanguage language: Language) -> [ProtoPattern] { + private class func copyPatternTree(patterns: [Pattern], parent: Pattern? = nil, foundPatterns: [Pattern: Pattern] = [:], inLanguage language: Language) -> [Pattern] { var newFoundPatterns = foundPatterns - var result: [ProtoPattern] = [] + var result: [Pattern] = [] for pattern in patterns { if let visitedPattern = foundPatterns[pattern] { result.append(visitedPattern) } else { - let newPattern: ProtoPattern + let newPattern: Pattern if pattern as? Include != nil && (pattern as! Include).type != .resolved { newPattern = Include(include: pattern as! Include, parent: parent) language.referenceManager.includes.append(newPattern as! Include) } else { - newPattern = ProtoPattern(pattern: pattern, parent: parent) + newPattern = Pattern(pattern: pattern, parent: parent) } newFoundPatterns[pattern] = newPattern newPattern.subpatterns = copyPatternTree(pattern.subpatterns, parent: newPattern, foundPatterns: newFoundPatterns, inLanguage: language) diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index dfff216..5b7e950 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -12,7 +12,7 @@ class Repository { // MARK: - Properties - private var entries: [String: ProtoPattern] = [:] + private var entries: [String: Pattern] = [:] private weak var parentRepository: Repository? @@ -28,7 +28,7 @@ class Repository { if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { subRepo = Repository(repo: containedRepo, inParent: self, inLanguage: language, withReferenceManager: refman) } - if let pattern = ProtoPattern(dictionary: value, parent: nil, withRepository: subRepo, withReferenceManager: refman) { + if let pattern = Pattern(dictionary: value, parent: nil, withRepository: subRepo, withReferenceManager: refman) { self.entries[key] = pattern } } @@ -37,7 +37,7 @@ class Repository { // MARK: - Accessing Patterns - subscript(index: String) -> ProtoPattern? { + subscript(index: String) -> Pattern? { if let resultAtLevel = entries[index] { return resultAtLevel } From 65693311786462d98b3cda904fbc30efb50128fb Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 18 Feb 2016 16:18:55 +0100 Subject: [PATCH 032/110] clean up public interface --- SyntaxKit/BundleManager.swift | 2 +- SyntaxKit/Language.swift | 24 ++++++++++++++---------- SyntaxKit/Theme.swift | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index c55419f..49abfef 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -24,7 +24,7 @@ public class BundleManager { defaultManager = BundleManager(callback: callback) } - init(callback: BundleLocationCallback) { + public init(callback: BundleLocationCallback) { self.bundleCallback = callback } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index c6fc15d..b288de7 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -10,12 +10,16 @@ import Foundation public class Language { - public var UUID: String = "" - public var name: String = "" - public var scopeName: String = "" - var referenceManager = ReferenceManager() + public var UUID: String {return _UUID} + public var name: String {return _name} + public var scopeName: String {return _scopeName} + + private var _UUID = "" + private var _name = "" + private var _scopeName = "" var pattern: Pattern = Pattern() + var referenceManager = ReferenceManager() var repository = Repository() static let globalScope = "GLOBAL" @@ -29,9 +33,9 @@ public class Language { array = dictionary["patterns"] as? [[NSObject: AnyObject]] else { return nil } - self.UUID = UUID - self.name = name - self.scopeName = scopeName + _UUID = UUID + _name = name + _scopeName = scopeName self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, inLanguage: self, withReferenceManager: referenceManager) referenceManager.resolveRepositoryReferences(repository) @@ -40,9 +44,9 @@ public class Language { func validateWithHelperLanguages(helperLanguages: [Language]) { let resolvedProtoLanguage = Language.resolveReferencesBetweenThisAndProtoLanguages(self, andOtherLanguages: helperLanguages) - self.UUID = resolvedProtoLanguage.UUID - self.name = resolvedProtoLanguage.name - self.scopeName = resolvedProtoLanguage.scopeName + _UUID = resolvedProtoLanguage.UUID + _name = resolvedProtoLanguage.name + _scopeName = resolvedProtoLanguage.scopeName self.pattern = resolvedProtoLanguage.pattern } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index c6888f7..34ec4de 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -40,7 +40,7 @@ public struct Theme { // MARK: - Initializers - public init?(dictionary: [NSObject: AnyObject]) { + init?(dictionary: [NSObject: AnyObject]) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, rawSettings = dictionary["settings"] as? [[String: AnyObject]] From de3e33bbcff97b832a42ce02b77dfd77e1ac9cbf Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 18 Feb 2016 18:57:08 +0100 Subject: [PATCH 033/110] consistency and formatting changes --- SyntaxKit.xcodeproj/project.pbxproj | 4 +-- SyntaxKit/BundleManager.swift | 52 ++++++++++++++++++----------- SyntaxKit/Language.swift | 34 +++++++++---------- SyntaxKit/Parser.swift | 4 --- SyntaxKit/Pattern.swift | 23 ++++++------- SyntaxKit/Theme.swift | 1 + 6 files changed, 62 insertions(+), 56 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index f5b76f9..b34997b 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -283,9 +283,8 @@ 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, - 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, - 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, 8CE64FE21C74B48D0007BA57 /* Language.swift */, + 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, @@ -294,6 +293,7 @@ 211989931B2EC38B00F0D786 /* Result.swift */, 8C6C24C71C6BD66A0087E156 /* Scope.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, + 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, 210299C11B2E8924009C61EE /* Resources */, 210299CD1B2E8924009C61EE /* Tests */, ); diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 49abfef..bef4bfe 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -10,37 +10,66 @@ import UIKit public class BundleManager { + // MARK: - Types + public typealias BundleLocationCallback = (identifier: String, isLanguage: Bool) -> (NSURL?) + + // MARK: - Properties + + public static var defaultManager: BundleManager? + private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] private var cachedLanguages: [String: Language] = [:] private var cachedProtoLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] - public static var defaultManager: BundleManager? + + // MARK: - Initializers public class func initializeGlobalManagerWithCallback(callback: BundleLocationCallback) { defaultManager = BundleManager(callback: callback) } - public init(callback: BundleLocationCallback) { + init(callback: BundleLocationCallback) { self.bundleCallback = callback } + + // MARK: - Public + public func getLanguageWithIdentifier(identifier: String) -> Language? { if let language = self.cachedLanguages[identifier] { return language } self.dependencies = [] - let language = self.getProtoLanguageWithIdentifier(identifier)! + var language = self.getProtoLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) self.cachedLanguages[identifier] = language return language } + public func getThemeWithIdentifier(identifier: String) -> Theme? { + if let theme = cachedThemes[identifier] { + return theme + } + + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), + plist = NSDictionary(contentsOfURL: dictURL), + newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { + return nil + } + + cachedThemes[identifier] = newTheme + return newTheme + } + + + // MARK: - Internal Interface + func getProtoLanguageWithIdentifier(identifier: String) -> Language? { if let language = cachedProtoLanguages[identifier] { if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { @@ -52,26 +81,11 @@ public class BundleManager { guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), plist = NSDictionary(contentsOfURL: dictURL), newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { - return nil + return nil } self.dependencies.append(newLanguage) cachedProtoLanguages[identifier] = newLanguage return newLanguage } - - public func getThemeWithIdentifier(identifier: String) -> Theme? { - if let theme = cachedThemes[identifier] { - return theme - } - - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), - plist = NSDictionary(contentsOfURL: dictURL), - newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { - return nil - } - - cachedThemes[identifier] = newTheme - return newTheme - } } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index b288de7..4b02118 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -8,15 +8,15 @@ import Foundation -public class Language { +public struct Language { - public var UUID: String {return _UUID} - public var name: String {return _name} - public var scopeName: String {return _scopeName} + public let UUID: String// {return _UUID} + public let name: String// {return _name} + public let scopeName: String// {return _scopeName} - private var _UUID = "" - private var _name = "" - private var _scopeName = "" +// private var _UUID = "" +// private var _name = "" +// private var _scopeName = "" var pattern: Pattern = Pattern() var referenceManager = ReferenceManager() @@ -33,32 +33,28 @@ public class Language { array = dictionary["patterns"] as? [[NSObject: AnyObject]] else { return nil } - _UUID = UUID - _name = name - _scopeName = scopeName + self.UUID = UUID + self.name = name + self.scopeName = scopeName self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, inLanguage: self, withReferenceManager: referenceManager) referenceManager.resolveRepositoryReferences(repository) referenceManager.resolveSelfReferences(self) } - func validateWithHelperLanguages(helperLanguages: [Language]) { - let resolvedProtoLanguage = Language.resolveReferencesBetweenThisAndProtoLanguages(self, andOtherLanguages: helperLanguages) - _UUID = resolvedProtoLanguage.UUID - _name = resolvedProtoLanguage.name - _scopeName = resolvedProtoLanguage.scopeName + mutating func validateWithHelperLanguages(helperLanguages: [Language]) { + let resolvedProtoLanguage = resolveReferencesBetweenThisAndProtoLanguages(helperLanguages) self.pattern = resolvedProtoLanguage.pattern } - private class func resolveReferencesBetweenThisAndProtoLanguages(thisLanguage: Language, andOtherLanguages otherLanguages: [Language]) -> Language { - let newLanguage = thisLanguage + private func resolveReferencesBetweenThisAndProtoLanguages(otherLanguages: [Language]) -> Language { + let newLanguage = self var copyOfProtoLanguages: [Language] = [] for language in otherLanguages { let newOtherLang = ReferenceManager.copyLanguage(language) copyOfProtoLanguages.append(newOtherLang) } - ReferenceManager.resolveInterLanguageReferences(copyOfProtoLanguages, basename: thisLanguage.scopeName) + ReferenceManager.resolveInterLanguageReferences(copyOfProtoLanguages, basename: self.scopeName) return newLanguage } - } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 379d48f..a8d89ae 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -404,10 +404,6 @@ public class Parser { return nil } - if (string as NSString).substringWithRange(bounds).containsString("@interface flickrCDTagsTableViewController ()") { -// print("string found") - } - var resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 346c4ad..872da91 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -192,22 +192,21 @@ class Include: Pattern { } func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { + let toReplacePattern: Pattern? if type == .toBase { - let baseLanguage = languages[baseName!]! - self.replaceWithPattern(baseLanguage.pattern) - self.type = .resolved + toReplacePattern = languages[baseName!]!.pattern } else if type == .toForeignRepository { - let newLanguage = languages[languageRef!] - if let pattern = newLanguage?.repository[repositoryRef!] { - self.replaceWithPattern(pattern) - } - self.type = .resolved + toReplacePattern = languages[languageRef!]?.repository[repositoryRef!] } else if type == .toForeign { - if let includedLang = languages[languageRef!] { - self.replaceWithPattern(includedLang.pattern) - } - self.type = .resolved + toReplacePattern = languages[languageRef!]?.pattern + } else { + return + } + + if toReplacePattern != nil { + self.replaceWithPattern(toReplacePattern!) } + self.type = .resolved } // MARK: - Private diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 34ec4de..bb58a8d 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -38,6 +38,7 @@ public struct Theme { } } + // MARK: - Initializers init?(dictionary: [NSObject: AnyObject]) { From 3d9904f523bf54400b25a5f55428b1bdd97ba3e9 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 18 Feb 2016 22:43:32 +0100 Subject: [PATCH 034/110] Code cleanup --- Readme.markdown | 13 +- SyntaxKit.xcodeproj/project.pbxproj | 8 - ...36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 2 +- ...39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8.plist | 45 +++++ .../Info.plist | 31 ++++ SyntaxKit/BundleManager.swift | 16 +- SyntaxKit/Language.swift | 48 +++--- SyntaxKit/Parser.swift | 8 +- SyntaxKit/Pattern.swift | 159 ++++++++++-------- SyntaxKit/RefernceManager.swift | 30 ++-- SyntaxKit/Repository.swift | 6 +- SyntaxKit/Resources/Info.plist | 2 +- SyntaxKit/Result.swift | 6 +- SyntaxKit/Scope.swift | 24 --- SyntaxKit/ScopedString.swift | 12 +- SyntaxKit/Tests/LanguageTests.swift | 18 +- SyntaxKit/Tests/TestHelper.swift | 5 +- 17 files changed, 242 insertions(+), 191 deletions(-) create mode 100644 SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8.plist delete mode 100644 SyntaxKit/Scope.swift diff --git a/Readme.markdown b/Readme.markdown index 211128a..3197020 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -4,7 +4,7 @@ SyntaxKit makes TextMate-style syntax highlighting easy. It works on iOS, watchOS, and OS X. -SyntaxKit was abstracted from [Whiskey](http://usewhiskey.com). +A fork from Sam Soffes' SyntaxKit which was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building @@ -40,9 +40,10 @@ Once you have a language, you can get started: ```swift import SyntaxKit -let path = "path to your .tmLanguage file" -let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] -let yaml = Language(dictionary: plist) +BundleManager.initializeDefaultManagerWithLocationCallback { identifier, isLanguage in + NSURL(string: "Location of Bundles/" + identifier + ".plist") +} +let yaml = BundleManager.defaultManager!.languageWithIdentifier("source.yaml")! let parser = Parser(language: yaml) ``` @@ -64,7 +65,7 @@ parser.parse(input) { scope, range in SyntaxKit also comes with `AttributedParser`. This is a simple subclass of `Parser` that knows how to work with themes. ```swift -let tomorrow = Theme(dictionary: themePlist) +let tomorrow = BundleManager.defaultManager!.themeWithIdentifier("tomorrow")! let attributedParser = AttributedParser(language: yaml, theme: tomorrow) attributedParser.parse(input) { scope, range, attributes in @@ -72,7 +73,7 @@ attributedParser.parse(input) { scope, range, attributes in } ``` -Notice that `attributes` is the third paramenter to the block now. This is a dictionary of attributes you can give to `NSAttributedString`. Other values may be included here that don't work with `NSAttributedString`. You can do your own inspection and do something custom if you want. +Notice that `attributes` is the third parameter to the block now. This is a dictionary of attributes you can give to `NSAttributedString`. `AttributedParser` includes a convenience method for turning a `String` of source code into an `NSAttributedString`: diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index b34997b..aaf3f00 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -55,9 +55,6 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; - 8C6C24C81C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; - 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; - 8C6C24CA1C6BD66A0087E156 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6C24C71C6BD66A0087E156 /* Scope.swift */; }; 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -153,7 +150,6 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; - 8C6C24C71C6BD66A0087E156 /* Scope.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Scope.swift; sourceTree = ""; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -291,7 +287,6 @@ 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, 211989941B2EC38B00F0D786 /* ResultSet.swift */, 211989931B2EC38B00F0D786 /* Result.swift */, - 8C6C24C71C6BD66A0087E156 /* Scope.swift */, 211989961B2EC38B00F0D786 /* Theme.swift */, 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, 210299C11B2E8924009C61EE /* Resources */, @@ -529,7 +524,6 @@ 211989C01B2EC40500F0D786 /* Capture.swift in Sources */, 211989C41B2EC40500F0D786 /* Parser.swift in Sources */, 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, - 8C6C24C91C6BD66A0087E156 /* Scope.swift in Sources */, 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */, @@ -571,7 +565,6 @@ 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, - 8C6C24C81C6BD66A0087E156 /* Scope.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, @@ -603,7 +596,6 @@ 2198CED31B36D5DE00BD463F /* Capture.swift in Sources */, 8CDD6F1D1C71594B0063915A /* BundleManager.swift in Sources */, 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */, - 8C6C24CA1C6BD66A0087E156 /* Scope.swift in Sources */, 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist index df048f0..2b2c420 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -21,7 +21,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.0026958 + 0.003 baselineIntegrationDisplayName Local Baseline diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8.plist new file mode 100644 index 0000000..a00fdb7 --- /dev/null +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8.plist @@ -0,0 +1,45 @@ + + + + + classNames + + IncrementalParsingTests + + testPerformanceEdgeCases() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.0023728 + baselineIntegrationDisplayName + Local Baseline + + + testPerformanceInScope() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.0030039 + baselineIntegrationDisplayName + Local Baseline + + + + SwiftBaselineHighlightingTests + + testHighlightingPerformance() + + com.apple.XCTPerformanceMetric_WallClockTime + + baselineAverage + 0.085269 + baselineIntegrationDisplayName + Local Baseline + + + + + + diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist index ee635f9..cb975cb 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/Info.plist @@ -35,6 +35,37 @@ com.apple.platform.iphonesimulator + 39CEC1B4-ED87-4CDB-A7E7-9A4ED4F3BEC8 + + localComputer + + busSpeedInMHz + 100 + cpuCount + 1 + cpuKind + 6-Core Intel Xeon E5 + cpuSpeedInMHz + 3500 + logicalCPUCoresPerPackage + 12 + modelCode + MacPro6,1 + physicalCPUCoresPerPackage + 6 + platformIdentifier + com.apple.platform.macosx + + targetArchitecture + x86_64 + targetDevice + + modelCode + iPhone8,2 + platformIdentifier + com.apple.platform.iphonesimulator + + diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index bef4bfe..9f6787e 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -22,13 +22,13 @@ public class BundleManager { private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] private var cachedLanguages: [String: Language] = [:] - private var cachedProtoLanguages: [String: Language] = [:] + private var cachedUnresolvedLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] // MARK: - Initializers - public class func initializeGlobalManagerWithCallback(callback: BundleLocationCallback) { + public class func initializeDefaultManagerWithLocationCallback(callback: BundleLocationCallback) { defaultManager = BundleManager(callback: callback) } @@ -39,20 +39,20 @@ public class BundleManager { // MARK: - Public - public func getLanguageWithIdentifier(identifier: String) -> Language? { + public func languageWithIdentifier(identifier: String) -> Language? { if let language = self.cachedLanguages[identifier] { return language } self.dependencies = [] - var language = self.getProtoLanguageWithIdentifier(identifier)! + var language = self.getUnvalidatedLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) self.cachedLanguages[identifier] = language return language } - public func getThemeWithIdentifier(identifier: String) -> Theme? { + public func themeWithIdentifier(identifier: String) -> Theme? { if let theme = cachedThemes[identifier] { return theme } @@ -70,8 +70,8 @@ public class BundleManager { // MARK: - Internal Interface - func getProtoLanguageWithIdentifier(identifier: String) -> Language? { - if let language = cachedProtoLanguages[identifier] { + func getUnvalidatedLanguageWithIdentifier(identifier: String) -> Language? { + if let language = cachedUnresolvedLanguages[identifier] { if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { self.dependencies.append(language) } @@ -85,7 +85,7 @@ public class BundleManager { } self.dependencies.append(newLanguage) - cachedProtoLanguages[identifier] = newLanguage + cachedUnresolvedLanguages[identifier] = newLanguage return newLanguage } } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 4b02118..981183d 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -2,25 +2,23 @@ // Language.swift // SyntaxKit // -// Created by Alexander Hedges on 17/02/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Created by Sam Soffes on 9/18/14. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. // import Foundation public struct Language { - public let UUID: String// {return _UUID} - public let name: String// {return _name} - public let scopeName: String// {return _scopeName} + // MARK: - Properties -// private var _UUID = "" -// private var _name = "" -// private var _scopeName = "" + public let UUID: String + public let name: String + public let scopeName: String - var pattern: Pattern = Pattern() - var referenceManager = ReferenceManager() - var repository = Repository() + let pattern: Pattern = Pattern() + let referenceManager = ReferenceManager() + let repository: Repository static let globalScope = "GLOBAL" @@ -36,25 +34,23 @@ public struct Language { self.UUID = UUID self.name = name self.scopeName = scopeName + self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) - self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, inLanguage: self, withReferenceManager: referenceManager) - referenceManager.resolveRepositoryReferences(repository) - referenceManager.resolveSelfReferences(self) + self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, withReferenceManager: referenceManager) + referenceManager.resolveInternalReferences(repository, inLanguage: self) } + /// Resolves all external reference the language has to the given languages. + /// Only after a call to this method the Language is fit for general use. + /// + /// - parameter helperLanguages: The languages that the language has + /// references to resolve agains. This should at least contain the + /// language itself. mutating func validateWithHelperLanguages(helperLanguages: [Language]) { - let resolvedProtoLanguage = resolveReferencesBetweenThisAndProtoLanguages(helperLanguages) - self.pattern = resolvedProtoLanguage.pattern - } - - private func resolveReferencesBetweenThisAndProtoLanguages(otherLanguages: [Language]) -> Language { let newLanguage = self - var copyOfProtoLanguages: [Language] = [] - for language in otherLanguages { - let newOtherLang = ReferenceManager.copyLanguage(language) - copyOfProtoLanguages.append(newOtherLang) - } - ReferenceManager.resolveInterLanguageReferences(copyOfProtoLanguages, basename: self.scopeName) - return newLanguage + let copyOfHelperLanguages = helperLanguages.map { ReferenceManager.copyLanguage($0) } + + ReferenceManager.resolveExternalReferencesBetweenLanguages(copyOfHelperLanguages, basename: self.scopeName) + self.pattern.subpatterns = newLanguage.pattern.subpatterns } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index a8d89ae..939598d 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -429,6 +429,10 @@ public class Parser { return resultSet } + // Implementation note: + // In this project the difference between a Result and a Scope is that + // the scope has the attribute set while the Result does not. + /// Uses the callback to communicate the result of the parsing pass back /// to the caller of parse. /// @@ -441,8 +445,8 @@ public class Parser { callback(scope: Language.globalScope, range: results.range) for result in results.results where result.range.length > 0 { - if let scope = result as? Scope { - self.scopesString?.addScopeAtBottom(scope) + if result.attribute != nil { + self.scopesString?.addScopeAtBottom(result as Scope) } else if result.patternIdentifier != "" { callback(scope: result.patternIdentifier, range: result.range) } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 872da91..92c26fd 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -18,16 +18,25 @@ class Pattern: NSObject { // MARK: - Properties - var name: String? - var match: NSRegularExpression? - var captures: CaptureCollection? - var begin: NSRegularExpression? - var beginCaptures: CaptureCollection? - var end: NSRegularExpression? - var endCaptures: CaptureCollection? - weak var parent: Pattern? + var name: String? { return _name } + var match: NSRegularExpression? { return _match } + var captures: CaptureCollection? { return _captures } + var begin: NSRegularExpression? { return _begin } + var beginCaptures: CaptureCollection? { return _beginCaptures } + var end: NSRegularExpression? { return _end } + var endCaptures: CaptureCollection? { return _endCaptures } + var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] + private var _name: String? + private var _match: NSRegularExpression? + private var _captures: CaptureCollection? + private var _begin: NSRegularExpression? + private var _beginCaptures: CaptureCollection? + private var _end: NSRegularExpression? + private var _endCaptures: CaptureCollection? + private weak var _parent: Pattern? + private let debug = true @@ -35,45 +44,45 @@ class Pattern: NSObject { init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { super.init() - self.parent = parent - self.name = dictionary["name"] as? String + _parent = parent + _name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - self.match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) + _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) if debug && self.match == nil { print("Problem parsing match expression \(matchExpr)") } } if let beginExpr = dictionary["begin"] as? String { - self.begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) if debug && self.begin == nil { print("Problem parsing begin expression \(beginExpr)") } } if let endExpr = dictionary["end"] as? String { - self.end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) if debug && self.end == nil { print("Problem parsing end expression \(endExpr)") } } if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { - self.beginCaptures = CaptureCollection(dictionary: dictionary) + _beginCaptures = CaptureCollection(dictionary: dictionary) } if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { if match != nil { - self.captures = CaptureCollection(dictionary: dictionary) + _captures = CaptureCollection(dictionary: dictionary) } else if begin != nil && end != nil { - self.beginCaptures = CaptureCollection(dictionary: dictionary) - self.endCaptures = self.beginCaptures + _beginCaptures = CaptureCollection(dictionary: dictionary) + _endCaptures = self.beginCaptures } } if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { - self.endCaptures = CaptureCollection(dictionary: dictionary) + _endCaptures = CaptureCollection(dictionary: dictionary) } if dictionary["match"] as? String != nil && self.match == nil { @@ -91,21 +100,21 @@ class Pattern: NSObject { } if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) + self.subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) } } init(pattern: Pattern, parent: Pattern?) { super.init() - self.name = pattern.name - self.match = pattern.match - self.captures = pattern.captures - self.begin = pattern.begin - self.beginCaptures = pattern.beginCaptures - self.end = pattern.end - self.endCaptures = pattern.endCaptures - self.parent = parent - subpatterns = [] + _name = pattern.name + _match = pattern.match + _captures = pattern.captures + _begin = pattern.begin + _beginCaptures = pattern.beginCaptures + _end = pattern.end + _endCaptures = pattern.endCaptures + _parent = parent + self.subpatterns = [] } override init() { @@ -124,102 +133,104 @@ enum referenceType { class Include: Pattern { - var type: referenceType + // MARK: - Properties + + var type: referenceType {return _type} + + private var _type: referenceType private let repositoryRef: String? private let languageRef: String? private let associatedRepository: Repository? + + // MARK: - Initializers + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { self.associatedRepository = repository if reference.hasPrefix("#") { - self.type = .toRepository + self._type = .toRepository self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) self.languageRef = nil } else if reference == "$self" { - self.type = .toSelf + self._type = .toSelf self.repositoryRef = nil self.languageRef = nil } else if reference == "$base" { - self.type = .toBase + self._type = .toBase self.repositoryRef = nil self.languageRef = nil } else if reference.containsString("#") { - self.type = .toForeignRepository + self._type = .toForeignRepository self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) - BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + BundleManager.defaultManager?.getUnvalidatedLanguageWithIdentifier(languageRef!) } else { - self.type = .toForeign + self._type = .toForeign self.repositoryRef = nil self.languageRef = reference - BundleManager.defaultManager?.getProtoLanguageWithIdentifier(languageRef!) + BundleManager.defaultManager?.getUnvalidatedLanguageWithIdentifier(languageRef!) } super.init() - self.parent = parent + _parent = parent } init(include: Include, parent: Pattern?) { - self.type = include.type + self._type = include.type self.repositoryRef = include.repositoryRef self.languageRef = include.languageRef self.associatedRepository = include.associatedRepository - super.init() - self.name = include.name - self.match = include.match - self.captures = include.captures - self.begin = include.begin - self.beginCaptures = include.beginCaptures - self.end = include.end - self.endCaptures = include.endCaptures - self.parent = parent - subpatterns = [] + super.init(pattern: include, parent: parent) } - func resolveRepositoryReferences(repository: Repository) { + + // MARK: - Reference Resolution + + func resolveInternalReference(repository: Repository, inLanguage language: Language) { + let pattern: Pattern? if type == .toRepository { - if let pattern = (associatedRepository ?? repository)[repositoryRef!] { - self.replaceWithPattern(pattern) - } - self.type = .resolved + pattern = (associatedRepository ?? repository)[repositoryRef!] + } else if type == .toSelf { + pattern = language.pattern + } else { + return } - } - - func resolveSelfReferences(language: Language) { - if type == .toSelf { - self.replaceWithPattern(language.pattern) - self.type = .resolved + + if pattern != nil { + self.replaceWithPattern(pattern!) } + _type = .resolved } func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { - let toReplacePattern: Pattern? + let pattern: Pattern? if type == .toBase { - toReplacePattern = languages[baseName!]!.pattern + pattern = languages[baseName!]!.pattern } else if type == .toForeignRepository { - toReplacePattern = languages[languageRef!]?.repository[repositoryRef!] + pattern = languages[languageRef!]?.repository[repositoryRef!] } else if type == .toForeign { - toReplacePattern = languages[languageRef!]?.pattern + pattern = languages[languageRef!]?.pattern } else { return } - if toReplacePattern != nil { - self.replaceWithPattern(toReplacePattern!) + if pattern != nil { + self.replaceWithPattern(pattern!) } - self.type = .resolved + _type = .resolved } + // MARK: - Private private func replaceWithPattern(pattern: Pattern) { - self.name = pattern.name - self.match = pattern.match - self.captures = pattern.captures - self.begin = pattern.begin - self.beginCaptures = pattern.beginCaptures - self.end = pattern.end - self.endCaptures = pattern.endCaptures - subpatterns = pattern.subpatterns + _name = pattern.name + _match = pattern.match + _captures = pattern.captures + _begin = pattern.begin + _beginCaptures = pattern.beginCaptures + _end = pattern.end + _endCaptures = pattern.endCaptures + self.subpatterns = pattern.subpatterns } } diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 96ad7ba..3aaebae 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -18,10 +18,13 @@ import Foundation class ReferenceManager { - var includes: [Include] = [] + // MARK: - Properties - init() {} + private var includes: [Include] = [] + + // MARK: - Pattern Creation and Resolution + func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { var results: [Pattern] = [] for rawPattern in patterns { @@ -36,19 +39,13 @@ class ReferenceManager { return results } - func resolveRepositoryReferences(repository: Repository) { - for include in includes where include.type == .toRepository { - include.resolveRepositoryReferences(repository) - } - } - - func resolveSelfReferences(language: Language) { - for include in includes where include.type == .toSelf { - include.resolveSelfReferences(language) + func resolveInternalReferences(repository: Repository, inLanguage language: Language) { + for include in includes { + include.resolveInternalReference(repository, inLanguage: language) } } - class func resolveInterLanguageReferences(languages: [Language], basename: String) { + class func resolveExternalReferencesBetweenLanguages(languages: [Language], basename: String) { var otherLanguages: [String: Language] = [:] for language in languages { otherLanguages[language.scopeName] = language @@ -61,14 +58,17 @@ class ReferenceManager { } } + + // MARK: - Language Copying + class func copyLanguage(language: Language) -> Language { let newLanguage = language newLanguage.referenceManager.includes = [] - newLanguage.pattern.subpatterns = ReferenceManager.copyPatternTree(language.pattern.subpatterns, inLanguage: newLanguage) + newLanguage.pattern.subpatterns = copyPatternsRecursively(language.pattern.subpatterns, inLanguage: newLanguage) return newLanguage } - private class func copyPatternTree(patterns: [Pattern], parent: Pattern? = nil, foundPatterns: [Pattern: Pattern] = [:], inLanguage language: Language) -> [Pattern] { + private class func copyPatternsRecursively(patterns: [Pattern], parent: Pattern? = nil, foundPatterns: [Pattern: Pattern] = [:], inLanguage language: Language) -> [Pattern] { var newFoundPatterns = foundPatterns var result: [Pattern] = [] for pattern in patterns { @@ -83,7 +83,7 @@ class ReferenceManager { newPattern = Pattern(pattern: pattern, parent: parent) } newFoundPatterns[pattern] = newPattern - newPattern.subpatterns = copyPatternTree(pattern.subpatterns, parent: newPattern, foundPatterns: newFoundPatterns, inLanguage: language) + newPattern.subpatterns = copyPatternsRecursively(pattern.subpatterns, parent: newPattern, foundPatterns: newFoundPatterns, inLanguage: language) result.append(newPattern) } } diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 5b7e950..ffbadc1 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -18,15 +18,13 @@ class Repository { // MARK: - Initializers - init() {} - - init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, inLanguage language: Language, withReferenceManager refman: ReferenceManager) { + init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, withReferenceManager refman: ReferenceManager) { self.parentRepository = parent for (key, value) in repo { var subRepo: Repository? if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { - subRepo = Repository(repo: containedRepo, inParent: self, inLanguage: language, withReferenceManager: refman) + subRepo = Repository(repo: containedRepo, inParent: self, withReferenceManager: refman) } if let pattern = Pattern(dictionary: value, parent: nil, withRepository: subRepo, withReferenceManager: refman) { self.entries[key] = pattern diff --git a/SyntaxKit/Resources/Info.plist b/SyntaxKit/Resources/Info.plist index be22a84..e876956 100644 --- a/SyntaxKit/Resources/Info.plist +++ b/SyntaxKit/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright - Copyright © 2015 Sam Soffes. All rights reserved. + Copyright © 2015 Sam Soffes. Copyright © 2016 Alexander Hedges. All rights reserved. NSPrincipalClass diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index 0edc2c0..fb2cec1 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -8,19 +8,21 @@ import Foundation -class Result: Equatable { +struct Result: Equatable { // MARK: - Properties let patternIdentifier: String var range: NSRange + let attribute: AnyObject? // MARK: - Initializers - init(identifier: String, range: NSRange) { + init(identifier: String, range: NSRange, attribute: AnyObject? = nil) { self.patternIdentifier = identifier self.range = range + self.attribute = attribute } } diff --git a/SyntaxKit/Scope.swift b/SyntaxKit/Scope.swift deleted file mode 100644 index 1461d6d..0000000 --- a/SyntaxKit/Scope.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Scope.swift -// SyntaxKit -// -// Created by Alexander Hedges on 10/02/16. -// Copyright © 2016 Sam Soffes. All rights reserved. -// - -import Foundation - -class Scope: Result { - - // MARK: - Properties - - let attribute: AnyObject? - - - // MARK: - Initializers - - init(identifier: String, range: NSRange, attribute: AnyObject? = nil) { - self.attribute = attribute - super.init(identifier: identifier, range: range) - } -} diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index fa67917..d5c145f 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -53,6 +53,8 @@ extension NSRange { } } +typealias Scope = Result + class ScopedString: NSObject, NSCopying { // MARK: - Properties @@ -77,15 +79,7 @@ class ScopedString: NSObject, NSCopying { func copyWithZone(zone: NSZone) -> AnyObject { let newScopedString = ScopedString(string: self.underlyingString) - newScopedString.levels = [] - for level in levels { - var newLevel: [Scope] = [] - for scope in level { - let newScope = Scope(identifier: scope.patternIdentifier, range: scope.range, attribute: scope.attribute) - newLevel.append(newScope) - } - newScopedString.levels.append(newLevel) - } + newScopedString.levels = levels return newScopedString } diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index a148aa1..ebfe9c9 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -23,16 +23,16 @@ class LanguageTests: XCTestCase { XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) - XCTAssertEqual("meta.embedded.line.ruby", yaml.patterns[0].name) - XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.patterns[0].beginCaptures?[0]?.name) - XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.patterns[0].endCaptures?[0]?.name) - XCTAssertEqual("punctuation.definition.comment.ruby", yaml.patterns[0].subpatterns[0].captures?[1]?.name) - XCTAssertEqual("string.unquoted.block.yaml", yaml.patterns[1].name) - XCTAssertEqual("punctuation.definition.entry.yaml", yaml.patterns[1].beginCaptures?[2]?.name) - XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.patterns[1].beginCaptures?[5]?.name) - XCTAssertEqual("constant.numeric.yaml", yaml.patterns[2].name) + XCTAssertEqual("meta.embedded.line.ruby", yaml.pattern.subpatterns[0].name) + XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.pattern.subpatterns[0].beginCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.pattern.subpatterns[0].endCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.comment.ruby", yaml.pattern.subpatterns[0].subpatterns[0].captures?[1]?.name) + XCTAssertEqual("string.unquoted.block.yaml", yaml.pattern.subpatterns[1].name) + XCTAssertEqual("punctuation.definition.entry.yaml", yaml.pattern.subpatterns[1].beginCaptures?[2]?.name) + XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.pattern.subpatterns[1].beginCaptures?[5]?.name) + XCTAssertEqual("constant.numeric.yaml", yaml.pattern.subpatterns[2].name) - let pattern = yaml.patterns[3] + let pattern = yaml.pattern.subpatterns[3] XCTAssertEqual("string.unquoted.yaml", pattern.name) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 854fe2f..b322430 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -18,8 +18,9 @@ func fixture(name: String, _ type: String) -> String! { func language(name: String) -> Language! { let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - let language = Language(dictionary: plist)! - return language.validatedLanguage() + var language = Language(dictionary: plist)! + language.validateWithHelperLanguages([]) + return language } func theme(name: String) -> Theme! { From 98b274ef39dd613e6b38df9f228920915a2606bb Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 25 Mar 2016 09:38:37 +0100 Subject: [PATCH 035/110] remove deprecations for Swift 3 --- SyntaxKit.xcodeproj/project.pbxproj | 1 + SyntaxKit/Parser.swift | 6 ++++-- SyntaxKit/ScopedString.swift | 18 +++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index aaf3f00..5049e65 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -772,6 +772,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = s; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 939598d..d060ffb 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -133,8 +133,9 @@ public class Parser { /// On nil the entire string will be parsed. /// - parameter callback: The callback to call on every match of a /// pattern identifier of the language - public func parse(string: String, var inRange bounds: NSRange? = nil, match callback: Callback) { + public func parse(string: String, inRange range: NSRange? = nil, match callback: Callback) { var endScope: Scope? = nil + var bounds = range if bounds == nil { bounds = NSRange(location: 0, length: (string as NSString).length) scopesString = ScopedString(string: string) @@ -303,7 +304,8 @@ public class Parser { } } - lineStart = lineEnd++ + lineStart = lineEnd + lineEnd += 1 } result.extendWithRange(NSRange(location: startIndex, length: stop - startIndex)) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index d5c145f..5ecbeb0 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -119,7 +119,7 @@ class ScopedString: NSObject, NSCopying { assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false - for var level = levels.count - 1; level >= 0; level-- { + for level in (levels.count - 1).stride(through: 0, by: -1) { if findScopeIntersectionWithRange(scope.range, atLevel: levels[level]) == nil { levels[level].insert(scope, atIndex: self.insertionPointForRange(scope.range, atLevel: levels[level])) added = true @@ -133,7 +133,7 @@ class ScopedString: NSObject, NSCopying { func topLevelScopeAtIndex(index: Int) -> Scope { let indexRange = NSRange(location: index, length: 1) - for var i = levels.count - 1; i >= 0; i-- { + for i in (levels.count - 1).stride(through: 0, by: -1) { let level = levels[i] if let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) { return theScope @@ -147,7 +147,7 @@ class ScopedString: NSObject, NSCopying { var foundScope = false let indexRange = NSRange(location: index, length: 1) - for var i = levels.count - 1; i >= 0; i-- { + for i in (levels.count - 1).stride(through: 0, by: -1) { let level = levels[i] let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) if theScope != nil { @@ -162,7 +162,7 @@ class ScopedString: NSObject, NSCopying { } func levelForScope(scope: Scope) -> Int { - for var i = 0; i < levels.count; i++ { + for i in 0 ..< levels.count { let level = levels[i] for currentScope in level { if scope == currentScope { @@ -179,8 +179,8 @@ class ScopedString: NSObject, NSCopying { func removeScopesInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) - for var level = levels.count-1; level >= 0; level-- { - for var scope = levels[level].count-1; scope >= 0; scope-- { + for level in (levels.count - 1).stride(through: 0, by: -1) { + for scope in (levels[level].count-1).stride(through: 0, by: -1) { let theScope = levels[level][scope] if NSIntersectionRange(theScope.range, range).length == theScope.range.length { levels[level].removeAtIndex(scope) @@ -213,8 +213,8 @@ class ScopedString: NSObject, NSCopying { let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString mutableString.deleteCharactersInRange(range) self.underlyingString = mutableString.copy() as! String - for var level = levels.count-1; level >= 0; level-- { - for var scope = levels[level].count-1; scope >= 0 ; scope-- { + for level in (levels.count - 1).stride(through: 0, by: -1) { + for scope in (levels[level].count-1).stride(through: 0, by: -1) { var theRange = levels[level][scope].range theRange.subtractRange(range) if theRange.isEmpty() { @@ -247,7 +247,7 @@ class ScopedString: NSObject, NSCopying { if range.location < scope.range.location { return i } - i++ + i += 1 } return i } From 22edaf5908d847be5577e96175ea135e69d2b1fc Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 28 Mar 2016 21:10:28 +0200 Subject: [PATCH 036/110] Update copyright information and add ability to abort --- SyntaxKit/AttributedParser.swift | 14 ++++--- SyntaxKit/Language.swift | 4 +- SyntaxKit/Parser.swift | 35 +++++++++++++---- SyntaxKit/Pattern.swift | 4 +- SyntaxKit/Repository.swift | 2 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 30 +++++++-------- SyntaxKit/Tests/ParserTests.swift | 38 ++++++++++--------- SyntaxKit/Tests/ScopedStringTests.swift | 2 +- .../SwiftBaselineHighlightingTests.swift | 2 +- SyntaxKit/Theme.swift | 4 +- 10 files changed, 83 insertions(+), 52 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 7aa1d59..d2a28a8 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -10,7 +10,7 @@ public class AttributedParser: Parser { // MARK: - Types - public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void + public typealias AttributedCallback = [(scope: String, range: NSRange, attributes: Attributes?)] -> Void // MARK: - Properties @@ -29,17 +29,19 @@ public class AttributedParser: Parser { // MARK: - Parsing public func parse(string: String, inRange bounds: NSRange? = nil, match callback: AttributedCallback) { - parse(string, inRange: bounds) { scope, range in - callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) + parse(string, inRange: bounds) { (results: [(scope: String, range: NSRange)]) in + callback(results.map {($0, $1, self.attributesForScope($0))}) } } public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { let output = NSMutableAttributedString(string: string, attributes: baseAttributes) output.beginEditing() - parse(string) { _, range, attributes in - if let attributes = attributes { - output.addAttributes(attributes, range: range) + parse(string) { (results: [(scope: String, range: NSRange, attributes: Attributes?)]) in + for result in results { + if let attributes = result.attributes { + output.addAttributes(attributes, range: result.range) + } } } output.endEditing() diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 981183d..78b3032 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -3,7 +3,9 @@ // SyntaxKit // // Created by Sam Soffes on 9/18/14. -// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// Copyright © 2014-2015 Sam Soffes. +// Copyright (c) 2016 Alexander Hedges. +// All rights reserved. // import Foundation diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index d060ffb..bd83c09 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -11,8 +11,9 @@ // for the range it should be reparsed on the given change. This range can then // be passed to parsed. // -// Created by Sam Soffes on 9/19/14. Edited by Alexander Hedges -// Copyright © 2014-2015 Sam Soffes. Copyright (C) 2016 Alexander Hedges. +// Created by Sam Soffes on 9/19/14. +// Copyright © 2014-2015 Sam Soffes. +// Copyright (c) 2016 Alexander Hedges. // All rights reserved. // @@ -22,7 +23,7 @@ public class Parser { // MARK: - Types - public typealias Callback = (scope: String, range: NSRange) -> Void + public typealias Callback = (results: [(scope: String, range: NSRange)]) -> Void // MARK: - Properties @@ -48,6 +49,8 @@ public class Parser { private var diff: (String?, NSRange)? + private var aborted = false + // MARK: - Initializers public init(language: Language) { @@ -57,6 +60,10 @@ public class Parser { // MARK: - Public + public func abortCurrentParsing() { + aborted = true + } + // Algorithmic notes: // If change occurred in a block reparse the lines in which the change // happened and the range of the block from this point on. If the change @@ -164,7 +171,10 @@ public class Parser { while startIndex < endIndex { let endPattern = endScope?.attribute as! Pattern? - let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) + guard let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) else { + aborted = false + return + } if endScope != nil { allResults.addResult(Result(identifier: endScope!.patternIdentifier, range: results.range)) @@ -270,7 +280,8 @@ public class Parser { /// /// - returns: The result set containing the lexical scope names with range /// information. May exceed stopIndex. - private func matchPatterns(patterns: [Pattern], withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet { + /// Only returns nil if the operation was aborted. + private func matchPatterns(patterns: [Pattern], withString string: String, withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet? { assert(endPattern == nil || endPattern!.end != nil) let s: NSString = string @@ -284,6 +295,9 @@ public class Parser { var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { + if aborted { + return nil + } let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) if endPattern != nil { @@ -368,7 +382,9 @@ public class Parser { } let newLocation = NSMaxRange(beginResults.range) - let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) + guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) else { + return nil + } var result = ResultSet(startingRange: endResults.range) if pattern.name != nil { result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) @@ -445,13 +461,16 @@ public class Parser { return } - callback(scope: Language.globalScope, range: results.range) + var cumulativeResults: [(scope: String, range: NSRange)] = [] + + cumulativeResults.append((Language.globalScope, results.range)) for result in results.results where result.range.length > 0 { if result.attribute != nil { self.scopesString?.addScopeAtBottom(result as Scope) } else if result.patternIdentifier != "" { - callback(scope: result.patternIdentifier, range: result.range) + cumulativeResults.append((result.patternIdentifier, result.range)) } } + callback(results: cumulativeResults) } } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 92c26fd..3899181 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -9,7 +9,9 @@ // resolved via the provided method. // // Created by Sam Soffes on 9/18/14. -// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// Copyright © 2014-2015 Sam Soffes. +// Copyright (c) 2016 Alexander Hedges. +// All rights reserved. // import Foundation diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index ffbadc1..3f5b112 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -3,7 +3,7 @@ // SyntaxKit // // Created by Alexander Hedges on 09/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import Foundation diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index c4cf1e4..ecafe1b 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -3,7 +3,7 @@ // SyntaxKit // // Created by Alexander Hedges on 27/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import XCTest @@ -27,7 +27,7 @@ class IncrementalParsingTests: XCTestCase { var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) XCTAssertEqual(rangeToParse, nil) - parser.parse(input, inRange: rangeToParse) { _, _ in return } + parser.parse(input, inRange: rangeToParse) { _ in return } var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) @@ -37,13 +37,13 @@ class IncrementalParsingTests: XCTestCase { rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) - parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + parser.parse(newInput, inRange: rangeToParse) { _ in return } newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) - parser.parse(input, inRange: rangeToParse) { _, _ in return } + parser.parse(input, inRange: rangeToParse) { _ in return } newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) @@ -57,62 +57,62 @@ class IncrementalParsingTests: XCTestCase { func testEdgeCase() { let input = "// test.swift\n/**" - parser.parse(input) { _, _ in return } + parser.parse(input) { _ in return } var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) - parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + parser.parse(newInput, inRange: rangeToParse) { _ in return } newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) - parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + parser.parse(newInput, inRange: rangeToParse) { _ in return } newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) - parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + parser.parse(newInput, inRange: rangeToParse) { _ in return } - parser.parse("") { _, _ in return } + parser.parse("") { _ in return } } func testPerformanceInScope() { let input = fixture("swifttest.swift", "txt") - self.parser.parse(input) { _, _ in return } + self.parser.parse(input) { _ in return } self.measureBlock { let newInput = stringByReplacingRange(NSRange(location: 239, length: 0), inString: input, withString: "Tests") var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 239, length: 5)) XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 24)) - self.parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + self.parser.parse(newInput, inRange: rangeToParse) { _ in return } rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: false, changedRange: NSRange(location: 239, length: 5)) XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 19)) - self.parser.parse(input, inRange: rangeToParse) { _, _ in return } + self.parser.parse(input, inRange: rangeToParse) { _ in return } } } func testPerformanceEdgeCases() { let input = fixture("swifttest.swift", "txt") - self.parser.parse(input) { _, _ in return } + self.parser.parse(input) { _ in return } self.measureBlock { let newInput = stringByReplacingRange(NSRange(location: 139, length: 1), inString: input, withString: "") var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 139, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 22)) - self.parser.parse(newInput, inRange: rangeToParse) { _, _ in return } + self.parser.parse(newInput, inRange: rangeToParse) { _ in return } rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 139, length: 1)) XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) - self.parser.parse(input, inRange: rangeToParse) { _, _ in return } + self.parser.parse(input, inRange: rangeToParse) { _ in return } } } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 58b094d..2bae22e 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -23,17 +23,19 @@ class ParserTests: XCTestCase { var punctuationBegin: NSRange? var punctuationEnd: NSRange? - parser.parse("title: \"Hello World\"\n") { scope, range in - if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { - stringQuoted = range - } - - if punctuationBegin == nil && scope.hasPrefix("punctuation.definition.string.begin") { - punctuationBegin = range - } - - if punctuationEnd == nil && scope.hasPrefix("punctuation.definition.string.end") { - punctuationEnd = range + parser.parse("title: \"Hello World\"\n") { (results: [(scope: String, range: NSRange)]) in + for result in results { + if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { + stringQuoted = result.range + } + + if punctuationBegin == nil && result.scope.hasPrefix("punctuation.definition.string.begin") { + punctuationBegin = result.range + } + + if punctuationEnd == nil && result.scope.hasPrefix("punctuation.definition.string.end") { + punctuationEnd = result.range + } } } @@ -45,9 +47,11 @@ class ParserTests: XCTestCase { func testParsingBeginEndCrap() { var stringQuoted: NSRange? - parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { scope, range in - if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { - stringQuoted = range + parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (results: [(scope: String, range: NSRange)]) in + for result in results { + if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { + stringQuoted = result.range + } } } @@ -55,13 +59,13 @@ class ParserTests: XCTestCase { } func testParsingGarbage() { - parser.parse("") { _, _ in } - parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } + parser.parse("") { _ in } + parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _ in } } func testRuby() { let parser = Parser(language: language("Ruby")) let input = fixture("test.rb", "txt") - parser.parse(input, match: { _, _ in return }) + parser.parse(input, match: { _ in return }) } } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 306bf71..9d9f81e 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -3,7 +3,7 @@ // SyntaxKit // // Created by Alexander Hedges on 30/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import XCTest diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index f47a9f0..1c53b8d 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -3,7 +3,7 @@ // SyntaxKit // // Created by Alexander Hedges on 19/01/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // import Foundation diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index bb58a8d..55bc0a7 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -3,7 +3,9 @@ // SyntaxKit // // Created by Sam Soffes on 10/11/14. -// Copyright © 2014-2015 Sam Soffes. All rights reserved. +// Copyright © 2014-2015 Sam Soffes. +// Copyright (c) Alexander Hedges. +// All rights reserved. // import Foundation From 2581e5f7ed363b6832726147723c65551787bd65 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Apr 2016 11:44:02 +0200 Subject: [PATCH 037/110] Factor out operation part of the parser --- SyntaxKit.xcodeproj/project.pbxproj | 8 ++++++++ SyntaxKit/AttributedParser.swift | 14 ++++++-------- SyntaxKit/Parser.swift | 16 +++++++++------- SyntaxKit/ParsingOperation.swift | 13 +++++++++++++ SyntaxKit/ScopedString.swift | 16 ++++++++++++---- SyntaxKit/Tests/ScopedStringTests.swift | 10 ++++++++++ 6 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 SyntaxKit/ParsingOperation.swift diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 5049e65..06ae354 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -55,6 +55,9 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8C10B6441CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; + 8C10B6451CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; + 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -150,6 +153,7 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; + 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParsingOperation.swift; sourceTree = ""; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -276,6 +280,7 @@ isa = PBXGroup; children = ( 211989951B2EC38B00F0D786 /* SyntaxKit.h */, + 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */, 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, @@ -528,6 +533,7 @@ 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, + 8C10B6451CC38E5200740E00 /* ParsingOperation.swift in Sources */, 8CDD6F1C1C71594B0063915A /* BundleManager.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, 211989C81B2EC40500F0D786 /* Theme.swift in Sources */, @@ -561,6 +567,7 @@ 8CDD6F1B1C71594B0063915A /* BundleManager.swift in Sources */, 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */, 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */, + 8C10B6441CC38E5200740E00 /* ParsingOperation.swift in Sources */, 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, @@ -599,6 +606,7 @@ 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, + 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index d2a28a8..7aa1d59 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -10,7 +10,7 @@ public class AttributedParser: Parser { // MARK: - Types - public typealias AttributedCallback = [(scope: String, range: NSRange, attributes: Attributes?)] -> Void + public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void // MARK: - Properties @@ -29,19 +29,17 @@ public class AttributedParser: Parser { // MARK: - Parsing public func parse(string: String, inRange bounds: NSRange? = nil, match callback: AttributedCallback) { - parse(string, inRange: bounds) { (results: [(scope: String, range: NSRange)]) in - callback(results.map {($0, $1, self.attributesForScope($0))}) + parse(string, inRange: bounds) { scope, range in + callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { let output = NSMutableAttributedString(string: string, attributes: baseAttributes) output.beginEditing() - parse(string) { (results: [(scope: String, range: NSRange, attributes: Attributes?)]) in - for result in results { - if let attributes = result.attributes { - output.addAttributes(attributes, range: result.range) - } + parse(string) { _, range, attributes in + if let attributes = attributes { + output.addAttributes(attributes, range: range) } } output.endEditing() diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index bd83c09..c76d834 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -23,7 +23,7 @@ public class Parser { // MARK: - Types - public typealias Callback = (results: [(scope: String, range: NSRange)]) -> Void + public typealias Callback = (scope: String, range: NSRange) -> Void // MARK: - Properties @@ -60,7 +60,7 @@ public class Parser { // MARK: - Public - public func abortCurrentParsing() { + func abortCurrentParsing() { aborted = true } @@ -199,6 +199,11 @@ public class Parser { } } scopesString!.removeScopesInRange(allResults.range) + if aborted { + aborted = false + diff = nil + return + } self.applyResults(allResults, callback: callback) diff = nil } @@ -461,16 +466,13 @@ public class Parser { return } - var cumulativeResults: [(scope: String, range: NSRange)] = [] - - cumulativeResults.append((Language.globalScope, results.range)) + callback(scope: Language.globalScope, range: results.range) for result in results.results where result.range.length > 0 { if result.attribute != nil { self.scopesString?.addScopeAtBottom(result as Scope) } else if result.patternIdentifier != "" { - cumulativeResults.append((result.patternIdentifier, result.range)) + callback(scope: result.patternIdentifier, range: result.range) } } - callback(results: cumulativeResults) } } diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift new file mode 100644 index 0000000..016ebb1 --- /dev/null +++ b/SyntaxKit/ParsingOperation.swift @@ -0,0 +1,13 @@ +// +// ParsingOperation.swift +// SyntaxKit +// +// Created by Alexander Hedges on 17/04/16. +// Copyright © 2016 Sam Soffes. All rights reserved. +// + +public class ParsingOperation: NSOperation { + + + +} diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 5ecbeb0..5efb694 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -37,6 +37,14 @@ extension NSRange { return index >= location && index <= location + length } + func partiallyContainsRange(otherRange: NSRange) -> Bool { + return otherRange.location + otherRange.length >= location && otherRange.location <= location + length + } + + func entirelyContainsRange(otherRange: NSRange) -> Bool { + return location <= otherRange.location && location + length >= otherRange.location + otherRange.length + } + mutating func subtractRange(range: NSRange) { length -= NSIntersectionRange(range, NSRange(location: location, length: length)).length if (range.location < self.location) { @@ -132,7 +140,7 @@ class ScopedString: NSObject, NSCopying { } func topLevelScopeAtIndex(index: Int) -> Scope { - let indexRange = NSRange(location: index, length: 1) + let indexRange = NSRange(location: index, length: 0) for i in (levels.count - 1).stride(through: 0, by: -1) { let level = levels[i] if let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) { @@ -146,7 +154,7 @@ class ScopedString: NSObject, NSCopying { assert(index >= 0 && index <= baseScope.range.length) var foundScope = false - let indexRange = NSRange(location: index, length: 1) + let indexRange = NSRange(location: index, length: 0) for i in (levels.count - 1).stride(through: 0, by: -1) { let level = levels[i] let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) @@ -182,7 +190,7 @@ class ScopedString: NSObject, NSCopying { for level in (levels.count - 1).stride(through: 0, by: -1) { for scope in (levels[level].count-1).stride(through: 0, by: -1) { let theScope = levels[level][scope] - if NSIntersectionRange(theScope.range, range).length == theScope.range.length { + if range.entirelyContainsRange(theScope.range) { levels[level].removeAtIndex(scope) } } @@ -234,7 +242,7 @@ class ScopedString: NSObject, NSCopying { private func findScopeIntersectionWithRange(range: NSRange, atLevel level: [Scope]) -> Scope? { for scope in level { - if NSIntersectionRange(scope.range, range).length != 0 { + if scope.range.partiallyContainsRange(range) { return scope } } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 9d9f81e..82da610 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -57,6 +57,16 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.underlyingString == "Tesssssst") XCTAssert(newScopedString.numberOfScopes() == 2) + let newScope3 = Scope(identifier: "zeroLengthScope1", range: NSRange(location: 0, length: 0), attribute: nil) + newScopedString.addScopeAtTop(newScope3) + let newScope4 = Scope(identifier: "zeroLengthScope2", range: NSRange(location: 3, length: 0), attribute: nil) + newScopedString.addScopeAtTop(newScope4) + XCTAssert(newScopedString.numberOfScopes() == 4) + XCTAssert(newScopedString.topLevelScopeAtIndex(0).patternIdentifier == "zeroLengthScope1") + + newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) + XCTAssert(newScopedString.numberOfScopes() == 3) + XCTAssert(newScopedString.topLevelScopeAtIndex(2).range == NSRange(location: 1, length: 8)) } From e6dbed0871515ebead2e398fa535bc706655a38d Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Apr 2016 13:53:37 +0200 Subject: [PATCH 038/110] make build universal again --- SyntaxKit.xcodeproj/project.pbxproj | 6 ++++++ SyntaxKit/BundleManager.swift | 2 -- SyntaxKit/ParsingOperation.swift | 10 +++++++++- SyntaxKit/Theme.swift | 14 ++++++++------ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 06ae354..d73e414 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -58,6 +58,9 @@ 8C10B6441CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; 8C10B6451CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; + 8C10B6471CC3937F00740E00 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8C10B6481CC3938100740E00 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; + 8C10B6491CC393E700740E00 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -571,6 +574,7 @@ 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, + 8C10B6471CC3937F00740E00 /* Color.swift in Sources */, 2119899D1B2EC38B00F0D786 /* Pattern.swift in Sources */, 211989A11B2EC38B00F0D786 /* Theme.swift in Sources */, 2119899E1B2EC38B00F0D786 /* Result.swift in Sources */, @@ -609,6 +613,8 @@ 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, + 8C10B6481CC3938100740E00 /* Color.swift in Sources */, + 8C10B6491CC393E700740E00 /* ScopedString.swift in Sources */, 2198CED81B36D5DE00BD463F /* Pattern.swift in Sources */, 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */, 2198CED91B36D5DE00BD463F /* Result.swift in Sources */, diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 9f6787e..860fb41 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -6,8 +6,6 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import UIKit - public class BundleManager { // MARK: - Types diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift index 016ebb1..5fa06a8 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/ParsingOperation.swift @@ -3,11 +3,19 @@ // SyntaxKit // // Created by Alexander Hedges on 17/04/16. -// Copyright © 2016 Sam Soffes. All rights reserved. +// Copyright © 2016 Alexander Hedges. All rights reserved. // public class ParsingOperation: NSOperation { + private let parser: AttributedParser + public init(language: Language, theme: Theme) { + parser = AttributedParser(language: language, theme: theme) + } + + public override func main() { +// parser.parse("test", match: nil) + } } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 55bc0a7..6026f70 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -12,6 +12,8 @@ import Foundation #if os(iOS) || os(watchOS) import UIKit +#else + import AppKit #endif public typealias Attributes = [String: AnyObject] @@ -24,19 +26,19 @@ public struct Theme { public let name: String public let attributes: [String: Attributes] - public var backgroundColor: UIColor { - if let color = attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? UIColor { + public var backgroundColor: Color { + if let color = attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color { return color } else { - return UIColor.whiteColor() + return Color.whiteColor() } } - public var foregroundColor: UIColor { - if let color = attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? UIColor { + public var foregroundColor: Color { + if let color = attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color { return color } else { - return UIColor.blackColor() + return Color.blackColor() } } From c78839d3db7408cc78a4eac2deecde46f38188af Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 21 May 2016 18:32:41 +0200 Subject: [PATCH 039/110] refactor parts from parser into parsingOperation Parsing operation is a subclass of NSOperation that integrates well with parser and encapsulates all the multithreaded functionality. --- SyntaxKit.xcodeproj/project.pbxproj | 2 +- SyntaxKit/AttributedParser.swift | 10 +- SyntaxKit/Parser.swift | 213 ++++-------------- SyntaxKit/ParsingOperation.swift | 157 ++++++++++++- SyntaxKit/ScopedString.swift | 4 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 166 +++++++------- SyntaxKit/Tests/ParserTests.swift | 8 +- 7 files changed, 291 insertions(+), 269 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index d73e414..6500705 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; - 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParsingOperation.swift; sourceTree = ""; }; + 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ParsingOperation.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 7aa1d59..25dba1a 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -28,8 +28,14 @@ public class AttributedParser: Parser { // MARK: - Parsing - public func parse(string: String, inRange bounds: NSRange? = nil, match callback: AttributedCallback) { - parse(string, inRange: bounds) { scope, range in + public func parse(string: String, match callback: AttributedCallback) { + parse(string) { scope, range in + callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) + } + } + + func parse(string: String, inRange range: NSRange?, withDiff diff: (String?, NSRange)?, inout usingPreviousScopesString scopes: ScopedString, match callback: AttributedCallback) { + parse(string, inRange: range, withDiff: diff, usingPreviousScopesString: &scopes) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index c76d834..3eda54d 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -28,28 +28,9 @@ public class Parser { // MARK: - Properties - public let language: Language - - // Contains the string previously passed to parse() if parse has already - // been called. It stores all the associated scopes with hierarchical - // information and ranges. The attributes of the scopes are values of type - // Pattern, the begin/end pattern associated with the scope. The scopes - // might not be fully populated after a call to parse with a limited range. - private var scopesString: ScopedString? - - // Contains information on the previously generated outdated range. This - // property is invalidated after every call to parse. The diff stores - // information on the change that was analysed in outdateRange and is used - // in parse. - // If the inspected change is an addition the string is set to the - // potentially inserted string and the range is set to - // (insertion, length: 0). - // If the inspected change is a deletion the string is nil and the range is - // the range that would potentially be deleted. - private var diff: (String?, NSRange)? + var aborted = false - - private var aborted = false + public let language: Language // MARK: - Initializers @@ -60,70 +41,19 @@ public class Parser { // MARK: - Public - func abortCurrentParsing() { - aborted = true + public func parse(string: String, match callback: Callback) { + if aborted { + return + } + var scope = ScopedString(string: string) + parse(string, inRange: nil, withDiff: nil, usingPreviousScopesString: &scope, match: callback) } - // Algorithmic notes: - // If change occurred in a block reparse the lines in which the change - // happened and the range of the block from this point on. If the change - // occurred in the global scope just reparse the lines that changed. - /// Returns the range in the given string that should be re-parsed after the - /// given change. - /// - /// This method returns a range that can be safely passed into parse so that - /// only a part of the string has to be reparsed. - /// In fact passing anything other than this range to parse might lead to - /// uninteded results but is not prohibited. - /// This method is only guaranteed to possibly not return nil if parse was - /// called on the old string before this call. The only kinds of changed - /// supported are single insertions and deletions of strings. - /// - /// - parameter newString: The examined new string. Should be the product - /// of previously parsed + change. - /// - parameter insertion: If the change applied to the old value is an - /// insertion as opposed to a deletion. - /// - parameter range: The range in which the change occurred. In case - /// of an insertion the range in the new string that - /// was inserted. For a deletion it is the range in - /// the old string that was deleted. - /// - /// - returns: A range in newString that can be safely re-parsed. Or nil if - /// everything has to be reparsed. - public func outdatedRangeForChangeInString(newString: String, changeIsInsertion insertion: Bool, changedRange range: NSRange) -> NSRange? { - if !stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range) { - return nil - } - - let potentialNewString = scopesString!.copy() as! ScopedString - - let s = newString as NSString - let linesRange: NSRange - if insertion { - potentialNewString.insertString(s.substringWithRange(range), atIndex: range.location) - diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) - linesRange = s.lineRangeForRange(range) - } else { - potentialNewString.deleteCharactersInRange(range) - diff = (nil, range) - linesRange = s.lineRangeForRange(NSRange(location: range.location, length: 0)) - } - if potentialNewString.underlyingString != newString { - return nil - } - - let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRange) - 1) - if scopeAtIndex == potentialNewString.baseScope { - return linesRange - } else { - let endOfCurrentScope = NSMaxRange(scopeAtIndex.range) - return NSUnionRange(linesRange, NSRange(location: range.location, length: endOfCurrentScope - range.location)) - } - } + // MARK: - Private // Implementation notes: - // The first part tries to find the context in which parsing should take + // The first part tries to find the context in which parsing should take // place (which block we are in), if any. // The second part parses the string the until the full range is consumed // and it may exceed that range if further parts of the string are outdated @@ -132,47 +62,45 @@ public class Parser { /// every match of a scope /// /// If a range is treated more of a recommendation than a requirement. - /// For best results supply a range that was returned from + /// For best results supply a range that was returned from /// outdatedRangeForChangeInString called before calling this method. /// /// - parameter string: The string that is parsed /// - parameter range: The range in which the string should be parsed. /// On nil the entire string will be parsed. + /// - parameter diff: Addition: "Added string", (insertionIndex, 0) + /// Deletion: nil, (deletionStart, deletionLength) + /// - parameter scopes: Denotes /// - parameter callback: The callback to call on every match of a /// pattern identifier of the language - public func parse(string: String, inRange range: NSRange? = nil, match callback: Callback) { + /// - returns: A scopedString that contains the range results of the parsing + /// Or nil if the parsing was aborted. + func parse(string: String, inRange range: NSRange?, withDiff diff: (String?, NSRange)?, inout usingPreviousScopesString scopes: ScopedString, match callback: Callback) { var endScope: Scope? = nil - var bounds = range - if bounds == nil { - bounds = NSRange(location: 0, length: (string as NSString).length) - scopesString = ScopedString(string: string) - } else if diffRepresentsChangesFromOldStringToNewString(string) { - endScope = self.scopesString!.topLevelScopeAtIndex(bounds!.location) + var bounds = range ?? NSRange(location: 0, length: (string as NSString).length) + var scopesString = scopes.copy() as! ScopedString + if range != nil && diff != nil { + endScope = scopesString.topLevelScopeAtIndex(bounds.location) if diff!.0 == nil { - scopesString!.deleteCharactersInRange(diff!.1) + scopesString.deleteCharactersInRange(diff!.1) } else { - scopesString!.insertString(diff!.0!, atIndex: diff!.1.location) + scopesString.insertString(diff!.0!, atIndex: diff!.1.location) } - if scopesString!.underlyingString != string { // recover from inconsistecy (for instance "." shortcut) + if scopesString.underlyingString != string { // recover from inconsistecy (for instance "." shortcut) print("Used the emergency trick") bounds = NSRange(location: 0, length: (string as NSString).length) endScope = nil scopesString = ScopedString(string: string) } - } else { - // here we don't guarantee best results, the user passed in a range we didn't give him - scopesString = ScopedString(string: string) - print("Warning: No guarantee for optimal results") } - var startIndex = bounds!.location - var endIndex = NSMaxRange(bounds!) - var allResults = ResultSet(startingRange: bounds!) + var startIndex = bounds.location + var endIndex = NSMaxRange(bounds) + var allResults = ResultSet(startingRange: bounds) while startIndex < endIndex { let endPattern = endScope?.attribute as! Pattern? guard let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withString: string, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) else { - aborted = false return } @@ -184,81 +112,26 @@ public class Parser { allResults.addResults(results) startIndex = NSMaxRange(results.range) if endScope != nil { - endScope = self.scopesString!.lowerScopeForScope(endScope!, AtIndex: startIndex) + endScope = scopesString.lowerScopeForScope(endScope!, AtIndex: startIndex) } } else { startIndex = endIndex } - if startIndex > endIndex && scopesString!.isInString(startIndex + 1) { - let scopeAtIndex = scopesString!.topLevelScopeAtIndex(startIndex + 1) - if endScope == nil && scopesString!.levelForScope(scopeAtIndex) > 0 || - endScope != nil && scopesString!.levelForScope(scopeAtIndex) > scopesString!.levelForScope(endScope!) { + if startIndex > endIndex && scopesString.isInString(startIndex + 1) { + let scopeAtIndex = scopesString.topLevelScopeAtIndex(startIndex + 1) + if endScope == nil && scopesString.levelForScope(scopeAtIndex) > 0 || + endScope != nil && scopesString.levelForScope(scopeAtIndex) > scopesString.levelForScope(endScope!) { endIndex = NSMaxRange(scopeAtIndex.range) } } } - scopesString!.removeScopesInRange(allResults.range) - if aborted { - aborted = false - diff = nil - return - } - self.applyResults(allResults, callback: callback) - diff = nil - } - - - // MARK: - Private - - // MARK: Range Helpers - - /// - returns: true if scopeString not nil and the number of characters - /// changed is consistent with the new string - private func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange) -> Bool { - if scopesString == nil { - return false - } - - var oldLength = newString.length - if insertion { - oldLength -= range.length - } else { - oldLength += range.length - } - - if (scopesString!.underlyingString as NSString).length != oldLength { - print("Warning: incompatible change") - return false - } - return true - } - - /// - returns: true if diff not nil and predicted change from diff matches - /// the characters from the new string in that range - private func diffRepresentsChangesFromOldStringToNewString(newStr: NSString) -> Bool { - if diff == nil { - print("Warning: Diff is nil") - return false - } - if diff!.0 == nil { - if !stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: diff!.1) { - return false - } - } else { - if !stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: diff!.1.location, length: (diff!.0! as NSString).length)) { - return false - } - if newStr.substringWithRange(NSRange(location: diff!.1.location, length: (diff!.0! as NSString).length)) != diff!.0! { - print("Warning: Passed in a wierd string") - return false - } + scopesString.removeScopesInRange(allResults.range) + scopes = scopesString + if !aborted { + self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) } - - return true } - - // MARK: Parsing // Algorithmic notes: // A pattern expression can not match a substring spanning multiple lines @@ -459,17 +332,15 @@ public class Parser { /// Uses the callback to communicate the result of the parsing pass back /// to the caller of parse. /// - /// - parameter resultSet: The results of the parsing pass - /// - parameter callback: The method to call on every successful match - private func applyResults(resultSet: ResultSet?, callback: Callback) { - guard let results = resultSet else { - return - } - + /// - parameter results: The results of the parsing pass + /// - parameter scopesString: The place to store the scopes + /// - parameter callback: The method to call on every successful + /// match + private func applyResults(results: ResultSet, inout storingInScopesString scopesString: ScopedString, callback: Callback) { callback(scope: Language.globalScope, range: results.range) for result in results.results where result.range.length > 0 { if result.attribute != nil { - self.scopesString?.addScopeAtBottom(result as Scope) + scopesString.addScopeAtBottom(result as Scope) } else if result.patternIdentifier != "" { callback(scope: result.patternIdentifier, range: result.range) } diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift index 5fa06a8..4100c5d 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/ParsingOperation.swift @@ -1,21 +1,170 @@ // -// ParsingOperation.swift +// AttributedParsingOperation.swift // SyntaxKit // // Created by Alexander Hedges on 17/04/16. // Copyright © 2016 Alexander Hedges. All rights reserved. // -public class ParsingOperation: NSOperation { +import Foundation + +public class AttributedParsingOperation: NSOperation { + + // MARK: - Types + + public typealias OperationCallback = [(range: NSRange, attributes: Attributes?)] -> Void + + + // MARK: - Properties private let parser: AttributedParser + private var stringToParse: String + private var operationCallback: OperationCallback + private var scopedStringResult: ScopedString + + private var range: NSRange? + private var diff: (String?, NSRange)? - public init(language: Language, theme: Theme) { + + // MARK: - Initializers + + public init(string: String, language: Language, theme: Theme, callback: OperationCallback) { + stringToParse = string parser = AttributedParser(language: language, theme: theme) + operationCallback = callback + scopedStringResult = ScopedString(string: string) + super.init() } + public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback) { + stringToParse = string + parser = previousOperation.parser + parser.aborted = false + operationCallback = callback + scopedStringResult = previousOperation.scopedStringResult + + super.init() + + let s = string as NSString + let diff: (String?, NSRange) + if insertion { + diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) + } else { + diff = (nil, range) + } + + if diffRepresentsChanges(diff, fromOldString: previousOperation.scopedStringResult.underlyingString, toNewString: string) { + self.diff = diff + self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range, previousScopedString: scopedStringResult) + } else { + assert(false, "Warning: The provided change information is inconsistent. The AttributedParsingOperation will parse the entire range") + } + } + + + // MARK: - NSOperation Implementation + public override func main() { -// parser.parse("test", match: nil) + var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] + parser.parse(stringToParse, inRange: range, withDiff: diff, usingPreviousScopesString: &scopedStringResult) { _, range, attributes in + if let attributes = attributes { + resultsArray.append((range, attributes)) + } + } + operationCallback(resultsArray) + } + + public override func cancel() { + parser.aborted = true + super.cancel() + } + + // MARK: - Change Processing + + // Algorithmic notes: + // If change occurred in a block reparse the lines in which the change + // happened and the range of the block from this point on. If the change + // occurred in the global scope just reparse the lines that changed. + + /// Returns the range in the given string that should be re-parsed after the + /// given change. + /// + /// This method returns a range that can be safely passed into parse so that + /// only a part of the string has to be reparsed. + /// In fact passing anything other than this range to parse might lead to + /// uninteded results but is not prohibited. + /// This method is only guaranteed to possibly not return nil if parse was + /// called on the old string before this call. The only kinds of changed + /// supported are single insertions and deletions of strings. + /// + /// - parameter newString: The examined new string. Should be the product + /// of previously parsed + change. + /// - parameter insertion: If the change applied to the old value is an + /// insertion as opposed to a deletion. + /// - parameter range: The range in which the change occurred. In case + /// of an insertion the range in the new string that + /// was inserted. For a deletion it is the range in + /// the old string that was deleted. + /// + /// - returns: A range in newString that can be safely re-parsed. Or nil if + /// everything has to be reparsed. + func outdatedRangeForChangeInString(newString: NSString, changeIsInsertion insertion: Bool, changedRange range: NSRange, previousScopedString previousScopes: ScopedString) -> NSRange? { + if !stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: previousScopes.underlyingString) { + return nil + } + + let potentialNewString = previousScopes.copy() as! ScopedString + + let linesRange: NSRange + if insertion { + potentialNewString.insertString(newString.substringWithRange(range), atIndex: range.location) + linesRange = newString.lineRangeForRange(range) + } else { + potentialNewString.deleteCharactersInRange(range) + linesRange = newString.lineRangeForRange(NSRange(location: range.location, length: 0)) + } + if potentialNewString.underlyingString != newString { + return nil + } + + let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRange) - 1) + if scopeAtIndex == potentialNewString.baseScope { + return linesRange + } else { + let endOfCurrentScope = NSMaxRange(scopeAtIndex.range) + return NSUnionRange(linesRange, NSRange(location: range.location, length: endOfCurrentScope - range.location)) + } } + /// - returns: true if diff not nil and predicted change from diff matches + /// the characters from the new string in that range + func diffRepresentsChanges(diff: (String?, NSRange), fromOldString oldString: NSString, toNewString newStr: NSString) -> Bool { + if diff.0 == nil { + if !stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: diff.1, oldString: oldString) { + return false + } + } else { + if !stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: diff.1.location, length: (diff.0! as NSString).length), oldString: oldString) { + return false + } + if newStr.substringWithRange(NSRange(location: diff.1.location, length: (diff.0! as NSString).length)) != diff.0! { + assert(false, "Warning: Passed in a wierd string") + return false + } + } + + return true + } + + /// - returns: true if scopeString not nil and the number of characters + /// changed is consistent with the new string + func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange, oldString: NSString) -> Bool { + let oldLength = insertion ? newString.length - range.length : newString.length + range.length + + if oldString.length != oldLength { + assert(false, "Warning: incompatible change") + return false + } + return true + } } diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 5efb694..36a3245 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -34,11 +34,11 @@ extension NSRange { } func containsIndex(index: Int) -> Bool { - return index >= location && index <= location + length + return length == 0 && index == location || index >= location && index < location + length } func partiallyContainsRange(otherRange: NSRange) -> Bool { - return otherRange.location + otherRange.length >= location && otherRange.location <= location + length + return otherRange.location + otherRange.length >= location && otherRange.location < location + length } func entirelyContainsRange(otherRange: NSRange) -> Bool { diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index ecafe1b..ac1daa3 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -22,97 +22,97 @@ class IncrementalParsingTests: XCTestCase { } func testEdits() { - let input = fixture("swifttest.swift", "txt") - - var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) - XCTAssertEqual(rangeToParse, nil) - - parser.parse(input, inRange: rangeToParse) { _ in return } - - var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 136)) - - newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) - - parser.parse(newInput, inRange: rangeToParse) { _ in return } - - newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) - - parser.parse(input, inRange: rangeToParse) { _ in return } - - newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) - XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) - - newInput = stringByReplacingRange(NSRange(location: 159, length: 0), inString: input, withString: "756") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) - XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) +// let input = fixture("swifttest.swift", "txt") +// +// var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) +// XCTAssertEqual(rangeToParse, nil) +// +// parser.parse(input, inRange: rangeToParse) { _ in return } +// +// var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 136)) +// +// newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) +// +// parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) +// +// parser.parse(input, inRange: rangeToParse) { _ in return } +// +// newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) +// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) +// +// newInput = stringByReplacingRange(NSRange(location: 159, length: 0), inString: input, withString: "756") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) +// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) } func testEdgeCase() { - let input = "// test.swift\n/**" - - parser.parse(input) { _ in return } - - var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") - var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) - - parser.parse(newInput, inRange: rangeToParse) { _ in return } - - newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) - - parser.parse(newInput, inRange: rangeToParse) { _ in return } - - newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") - rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) - - parser.parse(newInput, inRange: rangeToParse) { _ in return } - - parser.parse("") { _ in return } +// let input = "// test.swift\n/**" +// +// parser.parse(input) { _ in return } +// +// var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") +// var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) +// +// parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) +// +// parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") +// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) +// +// parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// parser.parse("") { _ in return } } func testPerformanceInScope() { - let input = fixture("swifttest.swift", "txt") - self.parser.parse(input) { _ in return } - - self.measureBlock { - let newInput = stringByReplacingRange(NSRange(location: 239, length: 0), inString: input, withString: "Tests") - var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 239, length: 5)) - XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 24)) - - self.parser.parse(newInput, inRange: rangeToParse) { _ in return } - - rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: false, changedRange: NSRange(location: 239, length: 5)) - XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 19)) - - self.parser.parse(input, inRange: rangeToParse) { _ in return } - } +// let input = fixture("swifttest.swift", "txt") +// self.parser.parse(input) { _ in return } +// +// self.measureBlock { +// let newInput = stringByReplacingRange(NSRange(location: 239, length: 0), inString: input, withString: "Tests") +// var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 239, length: 5)) +// XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 24)) +// +// self.parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: false, changedRange: NSRange(location: 239, length: 5)) +// XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 19)) +// +// self.parser.parse(input, inRange: rangeToParse) { _ in return } +// } } func testPerformanceEdgeCases() { - let input = fixture("swifttest.swift", "txt") - self.parser.parse(input) { _ in return } - - self.measureBlock { - let newInput = stringByReplacingRange(NSRange(location: 139, length: 1), inString: input, withString: "") - var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 139, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 22)) - - self.parser.parse(newInput, inRange: rangeToParse) { _ in return } - - rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 139, length: 1)) - XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) - - self.parser.parse(input, inRange: rangeToParse) { _ in return } - } +// let input = fixture("swifttest.swift", "txt") +// self.parser.parse(input) { _ in return } +// +// self.measureBlock { +// let newInput = stringByReplacingRange(NSRange(location: 139, length: 1), inString: input, withString: "") +// var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 139, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 22)) +// +// self.parser.parse(newInput, inRange: rangeToParse) { _ in return } +// +// rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 139, length: 1)) +// XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) +// +// self.parser.parse(input, inRange: rangeToParse) { _ in return } +// } } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 2bae22e..538c85f 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -23,8 +23,7 @@ class ParserTests: XCTestCase { var punctuationBegin: NSRange? var punctuationEnd: NSRange? - parser.parse("title: \"Hello World\"\n") { (results: [(scope: String, range: NSRange)]) in - for result in results { + parser.parse("title: \"Hello World\"\n") { (result: (scope: String, range: NSRange)) in if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { stringQuoted = result.range } @@ -36,7 +35,6 @@ class ParserTests: XCTestCase { if punctuationEnd == nil && result.scope.hasPrefix("punctuation.definition.string.end") { punctuationEnd = result.range } - } } XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) @@ -47,12 +45,10 @@ class ParserTests: XCTestCase { func testParsingBeginEndCrap() { var stringQuoted: NSRange? - parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (results: [(scope: String, range: NSRange)]) in - for result in results { + parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (result: (scope: String, range: NSRange)) in if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { stringQuoted = result.range } - } } XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) From 2cf64a0a99d0f408d31d149acd8bc9ce5ca5f5f8 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 22 May 2016 19:25:57 +0200 Subject: [PATCH 040/110] Fix various range related bugs And add a function to pretty-print a ScopedString for debugging purposes --- SyntaxKit/Parser.swift | 2 +- SyntaxKit/ScopedString.swift | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 3eda54d..e2cabe9 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -267,7 +267,7 @@ public class Parser { if pattern.name != nil { result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } - result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: beginResults.range.location + beginResults.range.length, length: result.range.length - beginResults.range.length), attribute: pattern)) + result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: beginResults.range.location + beginResults.range.length, length: NSUnionRange(beginResults.range, endResults.range).length - beginResults.range.length), attribute: pattern)) result.addResults(beginResults) result.addResults(endResults) return result diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 36a3245..33818eb 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -107,7 +107,7 @@ class ScopedString: NSObject, NSCopying { return index >= 0 && index <= baseScope.range.length } - func addScopeAtTop(scope: Scope) { + func addScopeAtBottom(scope: Scope) { assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false @@ -123,7 +123,7 @@ class ScopedString: NSObject, NSCopying { } } - func addScopeAtBottom(scope: Scope) { + func addScopeAtTop(scope: Scope) { assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false @@ -237,6 +237,34 @@ class ScopedString: NSObject, NSCopying { } } + func prettyPrint() { + var printableUnderlyingString = underlyingString.stringByReplacingOccurrencesOfString("\n", withString: "¬") + printableUnderlyingString = printableUnderlyingString.stringByReplacingOccurrencesOfString("\t", withString: "»") + print(printableUnderlyingString) + for level in (levels.count - 1).stride(through: 0, by: -1) { + var levelString = String(count: (underlyingString as NSString).length, repeatedValue: " " as Character) + for pattern in levels[level] { + let range = pattern.range + if range.length == 0 { + assert(false) + } else if range.length == 1 { + levelString = (levelString as NSString).stringByReplacingCharactersInRange(range, withString: "|") + } else { + let dashes = String(count: range.length - 2, repeatedValue: "-" as Character) + levelString = (levelString as NSString).stringByReplacingCharactersInRange(range, withString: "[\(dashes)]") + } + } + print(levelString) + } + var numberString = "" + for i in 0...(underlyingString as NSString).length/10 { + let numDigits = ("\(i*10)" as NSString).length + let dashes = String(count: 9 - numDigits, repeatedValue: "-" as Character) + numberString += "\(i*10)\(dashes)|" + } + print(numberString) + } + // MARK: - Private From e053134467926ffd006cea0f4f868cc06ff03ad1 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 20 Jun 2016 16:43:01 +0200 Subject: [PATCH 041/110] don't skip newlines --- SyntaxKit/Parser.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index e2cabe9..054d226 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -197,7 +197,6 @@ public class Parser { } lineStart = lineEnd - lineEnd += 1 } result.extendWithRange(NSRange(location: startIndex, length: stop - startIndex)) From 5376bcff15fece18e3c84343365c5bea0a0c93c8 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 20 Jun 2016 18:16:35 +0200 Subject: [PATCH 042/110] shallow search instead of deep search And other performance improvements like pruning the search tree by decreasing the search window (range). --- SyntaxKit/Parser.swift | 55 +++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 054d226..e1d08cb 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -176,19 +176,23 @@ public class Parser { if aborted { return nil } - let bestResultForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) + let bestPatternForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestResultForMiddle == nil || endMatchResult!.range.location < bestResultForMiddle!.range.location) { + if endMatchResult != nil && (bestPatternForMiddle == nil || endMatchResult!.range.location < bestPatternForMiddle!.1.location) { result.addResults(endMatchResult!) return result } } - if bestResultForMiddle != nil && bestResultForMiddle!.range.length != 0 { - result.addResults(bestResultForMiddle!) - let newStart = NSMaxRange(bestResultForMiddle!.range) + if bestPatternForMiddle != nil { + let resultForMiddle = matchPattern(bestPatternForMiddle!.0, inString: string, inRange: range)! + if resultForMiddle.range.length == 0 { + break + } + result.addResults(resultForMiddle) + let newStart = NSMaxRange(resultForMiddle.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) lineEnd = max(lineEnd, newStart) } else { @@ -219,17 +223,36 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> ResultSet? { - var bestResultForMiddle: ResultSet? + private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> (Pattern, NSRange)? { + var interestingBounds = bounds + var bestResult: (Pattern, NSRange)? for pattern in patterns { - let currRes = self.matchPattern(pattern, inString: string, inRange: bounds) - if currRes?.range.location == bounds.location { - return currRes - } else if bestResultForMiddle == nil || currRes != nil && currRes!.range.location < bestResultForMiddle!.range.location { - bestResultForMiddle = currRes + let currentMatch = self.firstMatchRangeOfPattern(pattern, inString: string, inRange: bounds) + if currentMatch?.1.location == bounds.location { + return currentMatch + } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.1.location < bestResult!.1.location) { + bestResult = currentMatch + interestingBounds.length = currentMatch!.1.location - interestingBounds.location + } + } + return bestResult + } + + private func firstMatchRangeOfPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> (Pattern, NSRange)? { + if let match = pattern.match { + if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + if resultSet.range.length != 0 { + return (pattern, resultSet.range) + } + } + } else if let begin = pattern.begin { + if let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) { + return (pattern, beginResults.range) } + } else if pattern.subpatterns.count >= 1 { + return findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) } - return bestResultForMiddle + return nil } // Implementation note: @@ -270,12 +293,6 @@ public class Parser { result.addResults(beginResults) result.addResults(endResults) return result - } else if pattern.subpatterns.count >= 1 { - var result = findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) - if pattern.name != nil { - result?.addResult(Result(identifier: pattern.name!, range: result!.range)) - } - return result } return nil } From 30cbb21447705ad0899b9a919a728e5e4390bb24 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 20 Jun 2016 18:31:18 +0200 Subject: [PATCH 043/110] change return to (pattern, start) --- SyntaxKit/Parser.swift | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index e1d08cb..a2ae6ed 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -176,18 +176,18 @@ public class Parser { if aborted { return nil } - let bestPatternForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) + let bestMatchForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestPatternForMiddle == nil || endMatchResult!.range.location < bestPatternForMiddle!.1.location) { + if endMatchResult != nil && (bestMatchForMiddle == nil || endMatchResult!.range.location < bestMatchForMiddle!.start) { result.addResults(endMatchResult!) return result } } - if bestPatternForMiddle != nil { - let resultForMiddle = matchPattern(bestPatternForMiddle!.0, inString: string, inRange: range)! + if bestMatchForMiddle != nil { + let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inString: string, inRange: range)! if resultForMiddle.range.length == 0 { break } @@ -223,31 +223,31 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> (Pattern, NSRange)? { + private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { var interestingBounds = bounds - var bestResult: (Pattern, NSRange)? + var bestResult: (pattern: Pattern, start: Int)? for pattern in patterns { - let currentMatch = self.firstMatchRangeOfPattern(pattern, inString: string, inRange: bounds) - if currentMatch?.1.location == bounds.location { + let currentMatch = self.firstMatchOfPattern(pattern, inString: string, inRange: bounds) + if currentMatch?.start == bounds.location { return currentMatch - } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.1.location < bestResult!.1.location) { + } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.start < bestResult!.start) { bestResult = currentMatch - interestingBounds.length = currentMatch!.1.location - interestingBounds.location + interestingBounds.length = currentMatch!.start - interestingBounds.location } } return bestResult } - private func firstMatchRangeOfPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> (Pattern, NSRange)? { + private func firstMatchOfPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { if let match = pattern.match { if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { - return (pattern, resultSet.range) + return (pattern, resultSet.range.location) } } } else if let begin = pattern.begin { if let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) { - return (pattern, beginResults.range) + return (pattern, beginResults.range.location) } } else if pattern.subpatterns.count >= 1 { return findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) From 08ae03a93fa7e7dce0c5d57dcc313c12665502c7 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 20 Jun 2016 20:45:03 +0200 Subject: [PATCH 044/110] don't use caching caching causes some wierd behaviour. other small fixes --- SyntaxKit.xcodeproj/project.pbxproj | 10 ++++----- SyntaxKit/BundleManager.swift | 33 +++++++++++++++-------------- SyntaxKit/Parser.swift | 9 ++++---- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 6500705..001bfb5 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -287,16 +287,16 @@ 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, + 211989941B2EC38B00F0D786 /* ResultSet.swift */, + 211989931B2EC38B00F0D786 /* Result.swift */, + 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, + 211989961B2EC38B00F0D786 /* Theme.swift */, 8CE64FE21C74B48D0007BA57 /* Language.swift */, + 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, - 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, - 211989941B2EC38B00F0D786 /* ResultSet.swift */, - 211989931B2EC38B00F0D786 /* Result.swift */, - 211989961B2EC38B00F0D786 /* Theme.swift */, - 8CDD6F1A1C71594B0063915A /* BundleManager.swift */, 210299C11B2E8924009C61EE /* Resources */, 210299CD1B2E8924009C61EE /* Tests */, ); diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 860fb41..cc5477f 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -19,8 +19,8 @@ public class BundleManager { private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] - private var cachedLanguages: [String: Language] = [:] - private var cachedUnresolvedLanguages: [String: Language] = [:] +// private var cachedLanguages: [String: Language] = [:] +// private var cachedUnresolvedLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] @@ -38,22 +38,22 @@ public class BundleManager { // MARK: - Public public func languageWithIdentifier(identifier: String) -> Language? { - if let language = self.cachedLanguages[identifier] { - return language - } +// if let language = self.cachedLanguages[identifier] { +// return language +// } self.dependencies = [] var language = self.getUnvalidatedLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) - self.cachedLanguages[identifier] = language +// self.cachedLanguages[identifier] = language return language } public func themeWithIdentifier(identifier: String) -> Theme? { - if let theme = cachedThemes[identifier] { - return theme - } +// if let theme = cachedThemes[identifier] { +// return theme +// } guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), plist = NSDictionary(contentsOfURL: dictURL), @@ -69,12 +69,13 @@ public class BundleManager { // MARK: - Internal Interface func getUnvalidatedLanguageWithIdentifier(identifier: String) -> Language? { - if let language = cachedUnresolvedLanguages[identifier] { - if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { - self.dependencies.append(language) - } - return language - } + print("Requested Unvalidated Language: \(identifier)") +// if let language = cachedUnresolvedLanguages[identifier] { +// if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { +// self.dependencies.append(language) +// } +// return language +// } guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), plist = NSDictionary(contentsOfURL: dictURL), @@ -83,7 +84,7 @@ public class BundleManager { } self.dependencies.append(newLanguage) - cachedUnresolvedLanguages[identifier] = newLanguage +// cachedUnresolvedLanguages[identifier] = newLanguage return newLanguage } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index a2ae6ed..f532deb 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -187,12 +187,12 @@ public class Parser { } if bestMatchForMiddle != nil { - let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inString: string, inRange: range)! - if resultForMiddle.range.length == 0 { + let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inString: string, inRange: range) + if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } - result.addResults(resultForMiddle) - let newStart = NSMaxRange(resultForMiddle.range) + result.addResults(resultForMiddle!) + let newStart = NSMaxRange(resultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) lineEnd = max(lineEnd, newStart) } else { @@ -285,6 +285,7 @@ public class Parser { guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) else { return nil } + var result = ResultSet(startingRange: endResults.range) if pattern.name != nil { result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) From 45b0b68089b0f2ef823fec6bf047f37e07601625 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 20 Jun 2016 21:26:19 +0200 Subject: [PATCH 045/110] remove comments --- SyntaxKit/BundleManager.swift | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index cc5477f..b2d64de 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -19,8 +19,6 @@ public class BundleManager { private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] -// private var cachedLanguages: [String: Language] = [:] -// private var cachedUnresolvedLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] @@ -38,23 +36,14 @@ public class BundleManager { // MARK: - Public public func languageWithIdentifier(identifier: String) -> Language? { -// if let language = self.cachedLanguages[identifier] { -// return language -// } - self.dependencies = [] var language = self.getUnvalidatedLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) -// self.cachedLanguages[identifier] = language return language } public func themeWithIdentifier(identifier: String) -> Theme? { -// if let theme = cachedThemes[identifier] { -// return theme -// } - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), plist = NSDictionary(contentsOfURL: dictURL), newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { @@ -69,14 +58,6 @@ public class BundleManager { // MARK: - Internal Interface func getUnvalidatedLanguageWithIdentifier(identifier: String) -> Language? { - print("Requested Unvalidated Language: \(identifier)") -// if let language = cachedUnresolvedLanguages[identifier] { -// if self.dependencies.indexOf({$0.UUID == language.UUID}) == nil { -// self.dependencies.append(language) -// } -// return language -// } - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), plist = NSDictionary(contentsOfURL: dictURL), newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { @@ -84,7 +65,6 @@ public class BundleManager { } self.dependencies.append(newLanguage) -// cachedUnresolvedLanguages[identifier] = newLanguage return newLanguage } } From 817a8d3312ed85bc454a313472554371b93f8243 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 21 Jun 2016 17:57:01 +0200 Subject: [PATCH 046/110] reintroduce language caching and other performance boosts Removes the copyLanguage which is big memory hog. The unvalidated languages are not cached for later use and so it seems ok to muck with the originals. --- SyntaxKit/BundleManager.swift | 46 +++++++++++++++++++++++++++------ SyntaxKit/Language.swift | 6 +---- SyntaxKit/RefernceManager.swift | 32 ----------------------- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index b2d64de..03578a8 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -15,17 +15,24 @@ public class BundleManager { // MARK: - Properties + public var languageCaching = true + public static var defaultManager: BundleManager? private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] + private var cachedLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] // MARK: - Initializers public class func initializeDefaultManagerWithLocationCallback(callback: BundleLocationCallback) { - defaultManager = BundleManager(callback: callback) + if defaultManager == nil { + defaultManager = BundleManager(callback: callback) + } else { + defaultManager!.bundleCallback = callback + } } init(callback: BundleLocationCallback) { @@ -36,14 +43,27 @@ public class BundleManager { // MARK: - Public public func languageWithIdentifier(identifier: String) -> Language? { + if let language = self.cachedLanguages[identifier] { + return language + } + self.dependencies = [] var language = self.getUnvalidatedLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) + if languageCaching { + self.cachedLanguages[identifier] = language + } + + self.dependencies = [] return language } public func themeWithIdentifier(identifier: String) -> Theme? { + if let theme = cachedThemes[identifier] { + return theme + } + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), plist = NSDictionary(contentsOfURL: dictURL), newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { @@ -54,17 +74,27 @@ public class BundleManager { return newTheme } + public func emptyLanguageCache() { + self.cachedLanguages = [:] + } + // MARK: - Internal Interface func getUnvalidatedLanguageWithIdentifier(identifier: String) -> Language? { - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), - plist = NSDictionary(contentsOfURL: dictURL), - newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { - return nil - } + let indexOfStoredLanguage = self.dependencies.indexOf{ (lang: Language) in lang.scopeName == identifier } - self.dependencies.append(newLanguage) - return newLanguage + if indexOfStoredLanguage != nil { + return self.dependencies[indexOfStoredLanguage!] + } else { + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), + plist = NSDictionary(contentsOfURL: dictURL), + newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { + return nil + } + + self.dependencies.append(newLanguage) + return newLanguage + } } } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 78b3032..c53cb95 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -49,10 +49,6 @@ public struct Language { /// references to resolve agains. This should at least contain the /// language itself. mutating func validateWithHelperLanguages(helperLanguages: [Language]) { - let newLanguage = self - let copyOfHelperLanguages = helperLanguages.map { ReferenceManager.copyLanguage($0) } - - ReferenceManager.resolveExternalReferencesBetweenLanguages(copyOfHelperLanguages, basename: self.scopeName) - self.pattern.subpatterns = newLanguage.pattern.subpatterns + ReferenceManager.resolveExternalReferencesBetweenLanguages(helperLanguages, basename: self.scopeName) } } diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 3aaebae..e3922de 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -57,36 +57,4 @@ class ReferenceManager { } } } - - - // MARK: - Language Copying - - class func copyLanguage(language: Language) -> Language { - let newLanguage = language - newLanguage.referenceManager.includes = [] - newLanguage.pattern.subpatterns = copyPatternsRecursively(language.pattern.subpatterns, inLanguage: newLanguage) - return newLanguage - } - - private class func copyPatternsRecursively(patterns: [Pattern], parent: Pattern? = nil, foundPatterns: [Pattern: Pattern] = [:], inLanguage language: Language) -> [Pattern] { - var newFoundPatterns = foundPatterns - var result: [Pattern] = [] - for pattern in patterns { - if let visitedPattern = foundPatterns[pattern] { - result.append(visitedPattern) - } else { - let newPattern: Pattern - if pattern as? Include != nil && (pattern as! Include).type != .resolved { - newPattern = Include(include: pattern as! Include, parent: parent) - language.referenceManager.includes.append(newPattern as! Include) - } else { - newPattern = Pattern(pattern: pattern, parent: parent) - } - newFoundPatterns[pattern] = newPattern - newPattern.subpatterns = copyPatternsRecursively(pattern.subpatterns, parent: newPattern, foundPatterns: newFoundPatterns, inLanguage: language) - result.append(newPattern) - } - } - return result - } } From 463ca7da7ccdfaa460e701bee08806fdee039436 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 21 Jun 2016 20:18:08 +0200 Subject: [PATCH 047/110] make end prioritized over middle match --- SyntaxKit/Parser.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index f532deb..f8315d8 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -180,7 +180,7 @@ public class Parser { if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestMatchForMiddle == nil || endMatchResult!.range.location < bestMatchForMiddle!.start) { + if endMatchResult != nil && (bestMatchForMiddle == nil || endMatchResult!.range.location <= bestMatchForMiddle!.start) { result.addResults(endMatchResult!) return result } From cf12281e936a5ddb2b26e5a7b42ca8f053fcd816 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 23 Jun 2016 19:44:03 +0200 Subject: [PATCH 048/110] make parser use less memory in a stackframe Also adds support for the applyEndPatternLast key in a pattern --- .../xcschemes/SyntaxKit-OSX.xcscheme | 2 +- .../xcschemes/SyntaxKit-iOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-watchOS.xcscheme | 2 +- SyntaxKit/AttributedParser.swift | 4 +- SyntaxKit/Parser.swift | 70 +++++++++++-------- SyntaxKit/ParsingOperation.swift | 4 +- SyntaxKit/Pattern.swift | 4 ++ SyntaxKit/ResultSet.swift | 8 +-- 8 files changed, 57 insertions(+), 39 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-OSX.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-OSX.xcscheme index 8fa92ed..8d9bac9 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-OSX.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-OSX.xcscheme @@ -1,6 +1,6 @@ ResultSet? { + private func matchPatterns(patterns: [Pattern], withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet? { assert(endPattern == nil || endPattern!.end != nil) - let s: NSString = string - assert(s.length >= stop) + assert((string as NSString).length >= stop) var lineStart = startIndex var lineEnd = startIndex - var result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) + let result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) + + var bestMatchForMiddle: (pattern: Pattern, start: Int)? + var endMatchResult: ResultSet? + var resultForMiddle: ResultSet? + var range: NSRange + var newStart: Int while lineEnd < stop { - s.getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) - var range = NSRange(location: lineStart, length: lineEnd - lineStart) + (string as NSString).getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) + range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { if aborted { return nil } - let bestMatchForMiddle = findBestPatternInPatterns(patterns, inString: string, inRange: range) + + bestMatchForMiddle = findBestPatternInPatterns(patterns, inRange: range) if endPattern != nil { - let endMatchResult = self.matchExpression(endPattern!.end!, withString: string, inRange: range, captures: endPattern!.endCaptures) - if endMatchResult != nil && (bestMatchForMiddle == nil || endMatchResult!.range.location <= bestMatchForMiddle!.start) { + endMatchResult = self.matchExpression(endPattern!.end!, inRange: range, captures: endPattern!.endCaptures) + if endMatchResult != nil && (bestMatchForMiddle == nil || bestMatchForMiddle != nil && + (!endPattern!.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.start || endMatchResult!.range.location < bestMatchForMiddle!.start)) { result.addResults(endMatchResult!) return result } } if bestMatchForMiddle != nil { - let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inString: string, inRange: range) + resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inRange: range) if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } result.addResults(resultForMiddle!) - let newStart = NSMaxRange(resultForMiddle!.range) + newStart = NSMaxRange(resultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) lineEnd = max(lineEnd, newStart) } else { @@ -223,11 +235,11 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inString string: String, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { + private func findBestPatternInPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { var interestingBounds = bounds var bestResult: (pattern: Pattern, start: Int)? for pattern in patterns { - let currentMatch = self.firstMatchOfPattern(pattern, inString: string, inRange: bounds) + let currentMatch = self.firstMatchOfPattern(pattern, inRange: bounds) if currentMatch?.start == bounds.location { return currentMatch } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.start < bestResult!.start) { @@ -238,19 +250,19 @@ public class Parser { return bestResult } - private func firstMatchOfPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { + private func firstMatchOfPattern(pattern: Pattern, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { if let match = pattern.match { - if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { return (pattern, resultSet.range.location) } } } else if let begin = pattern.begin { - if let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) { + if let beginResults = matchExpression(begin, inRange: bounds, captures: pattern.beginCaptures) { return (pattern, beginResults.range.location) } } else if pattern.subpatterns.count >= 1 { - return findBestPatternInPatterns(pattern.subpatterns, inString: string, inRange: bounds) + return findBestPatternInPatterns(pattern.subpatterns, inRange: bounds) } return nil } @@ -269,24 +281,24 @@ public class Parser { /// matched successfully /// /// - returns: The result of the match. Nil if unsuccessful - private func matchPattern(pattern: Pattern, inString string: String, inRange bounds: NSRange) -> ResultSet? { + private func matchPattern(pattern: Pattern, inRange bounds: NSRange) -> ResultSet? { if let match = pattern.match { - if let resultSet = matchExpression(match, withString: string, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { return resultSet } } } else if let begin = pattern.begin, _ = pattern.end { - guard let beginResults = matchExpression(begin, withString: string, inRange: bounds, captures: pattern.beginCaptures) else { + guard let beginResults = matchExpression(begin, inRange: bounds, captures: pattern.beginCaptures) else { return nil } let newLocation = NSMaxRange(beginResults.range) - guard let endResults = matchPatterns(pattern.subpatterns, withString: string, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) else { + guard let endResults = matchPatterns(pattern.subpatterns, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) else { return nil } - var result = ResultSet(startingRange: endResults.range) + let result = ResultSet(startingRange: endResults.range) if pattern.name != nil { result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) } @@ -312,12 +324,12 @@ public class Parser { /// - returns: The set containing the results. May be nil if the expression /// could not match any part of the string. It may also be empty /// and only contain range information to show what it matched. - private func matchExpression(regularExpression: NSRegularExpression, withString string: String, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { + private func matchExpression(regularExpression: NSRegularExpression, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { return nil } - var resultSet = ResultSet(startingRange: result.range) + let resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) } diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift index 4100c5d..30b626a 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/ParsingOperation.swift @@ -66,12 +66,14 @@ public class AttributedParsingOperation: NSOperation { public override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] - parser.parse(stringToParse, inRange: range, withDiff: diff, usingPreviousScopesString: &scopedStringResult) { _, range, attributes in + parser.string = stringToParse + parser.parse(inRange: range, withDiff: diff, usingPreviousScopesString: &scopedStringResult) { _, range, attributes in if let attributes = attributes { resultsArray.append((range, attributes)) } } operationCallback(resultsArray) + parser.string = "" } public override func cancel() { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 3899181..5d74b64 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -27,6 +27,7 @@ class Pattern: NSObject { var beginCaptures: CaptureCollection? { return _beginCaptures } var end: NSRegularExpression? { return _end } var endCaptures: CaptureCollection? { return _endCaptures } + var applyEndPatternLast: Bool { return _applyEndPatternLast} var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] @@ -37,6 +38,7 @@ class Pattern: NSObject { private var _beginCaptures: CaptureCollection? private var _end: NSRegularExpression? private var _endCaptures: CaptureCollection? + private var _applyEndPatternLast = false private weak var _parent: Pattern? private let debug = true @@ -70,6 +72,8 @@ class Pattern: NSObject { } } + _applyEndPatternLast = dictionary["applyEndPatternLast"] as? Bool ?? false + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { _beginCaptures = CaptureCollection(dictionary: dictionary) } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 81e920b..f815016 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -8,7 +8,7 @@ import Foundation -struct ResultSet { +class ResultSet { // MARK: - Properties @@ -28,16 +28,16 @@ struct ResultSet { // MARK: - Adding - mutating func extendWithRange(range: NSRange) { + func extendWithRange(range: NSRange) { _range = NSUnionRange(self.range, range) } - mutating func addResult(result: Result) { + func addResult(result: Result) { _results.append(result) extendWithRange(result.range) } - mutating func addResults(resultSet: ResultSet) { + func addResults(resultSet: ResultSet) { extendWithRange(resultSet.range) for result in resultSet.results { _results.append(result) From 049bf1eec7783d6853abd4151ef25ed1aac86d90 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 23 Jun 2016 23:28:46 +0200 Subject: [PATCH 049/110] add caveats --- Readme.markdown | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Readme.markdown b/Readme.markdown index 3197020..c4c9fa7 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -89,3 +89,17 @@ Easy as that. This method takes an optional `baseAttributes` parameter to custom If you want to build your own parser (for example, to generate HTML) you can subclass whichever one meets your needs. Go nuts. Enjoy. + +## A list of Caveats + +There are however a few things you got to watch out for: + +* \G will always be matched +* Technical differences from onigurama to NSRegularExpression +* Backreferences to begin are not supported +* contentName property is not supported +* Attributes other than foreground color are ignored +* crashs after ~ 300 stackframes in a secondary thread (in debug configuration) +* Cannot recursively include itself (use $self or $base instead) + +Feel free to improve upon this. From 21504a1833826518aff3456360ad4490ef98df59 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 27 Jun 2016 00:26:24 +0200 Subject: [PATCH 050/110] Remove unneeded assertions --- SyntaxKit/ParsingOperation.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift index 30b626a..b7397d6 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/ParsingOperation.swift @@ -56,8 +56,6 @@ public class AttributedParsingOperation: NSOperation { if diffRepresentsChanges(diff, fromOldString: previousOperation.scopedStringResult.underlyingString, toNewString: string) { self.diff = diff self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range, previousScopedString: scopedStringResult) - } else { - assert(false, "Warning: The provided change information is inconsistent. The AttributedParsingOperation will parse the entire range") } } @@ -150,7 +148,6 @@ public class AttributedParsingOperation: NSOperation { return false } if newStr.substringWithRange(NSRange(location: diff.1.location, length: (diff.0! as NSString).length)) != diff.0! { - assert(false, "Warning: Passed in a wierd string") return false } } @@ -164,7 +161,6 @@ public class AttributedParsingOperation: NSOperation { let oldLength = insertion ? newString.length - range.length : newString.length + range.length if oldString.length != oldLength { - assert(false, "Warning: incompatible change") return false } return true From 08aba2517c59439d0daccc2c731db162167ba07a Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Wed, 29 Jun 2016 18:15:34 +0200 Subject: [PATCH 051/110] Completely disable bitcode --- SyntaxKit.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 001bfb5..7942117 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -733,6 +733,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -782,6 +783,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; From da6ab3bb5fb940121017670374fa6a475048df0c Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 3 Jul 2016 18:21:06 +0200 Subject: [PATCH 052/110] change cache clearing signature --- SyntaxKit/BundleManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 03578a8..a24c07b 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -74,7 +74,7 @@ public class BundleManager { return newTheme } - public func emptyLanguageCache() { + public func clearLanguageCache() { self.cachedLanguages = [:] } From ecb01b6a309f2c02e6d3fc9e4c47a8e32e1ddc53 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 11 Jul 2016 19:23:28 +0200 Subject: [PATCH 053/110] include what you use and make copyright implicit --- SyntaxKit/AttributedParser.swift | 2 ++ SyntaxKit/Capture.swift | 2 -- SyntaxKit/CaptureCollection.swift | 2 -- SyntaxKit/Color.swift | 2 +- SyntaxKit/Language.swift | 6 +----- SyntaxKit/Parser.swift | 6 +----- SyntaxKit/ParsingOperation.swift | 2 -- SyntaxKit/Pattern.swift | 6 +----- SyntaxKit/RefernceManager.swift | 2 -- SyntaxKit/Repository.swift | 2 -- SyntaxKit/Result.swift | 2 -- SyntaxKit/ResultSet.swift | 2 -- SyntaxKit/Tests/ScopedStringTests.swift | 3 +-- SyntaxKit/Theme.swift | 6 +----- 14 files changed, 8 insertions(+), 37 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 8c47ab9..2f92c43 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -6,6 +6,8 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // +import Foundation + public class AttributedParser: Parser { // MARK: - Types diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index a3bd64a..4ad7635 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -6,8 +6,6 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - struct Capture { // MARK: - Properties diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index fb538ad..b1bb05b 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -6,8 +6,6 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - struct CaptureCollection { // MARK: - Properties diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index 9de28ec..b1e62bb 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -3,7 +3,7 @@ // X // // Created by Sam Soffes on 4/28/15. -// Copyright (c) 2015 Sam Soffes. All rights reserved. +// Copyright © 2015 Sam Soffes. All rights reserved. // #if os(OSX) diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index c53cb95..d25827e 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -3,13 +3,9 @@ // SyntaxKit // // Created by Sam Soffes on 9/18/14. -// Copyright © 2014-2015 Sam Soffes. -// Copyright (c) 2016 Alexander Hedges. -// All rights reserved. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - public struct Language { // MARK: - Properties diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 692fe19..82b8996 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -12,13 +12,9 @@ // be passed to parsed. // // Created by Sam Soffes on 9/19/14. -// Copyright © 2014-2015 Sam Soffes. -// Copyright (c) 2016 Alexander Hedges. -// All rights reserved. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - public class Parser { // MARK: - Types diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/ParsingOperation.swift index b7397d6..a60879b 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/ParsingOperation.swift @@ -6,8 +6,6 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import Foundation - public class AttributedParsingOperation: NSOperation { // MARK: - Types diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 5d74b64..64e39af 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -9,13 +9,9 @@ // resolved via the provided method. // // Created by Sam Soffes on 9/18/14. -// Copyright © 2014-2015 Sam Soffes. -// Copyright (c) 2016 Alexander Hedges. -// All rights reserved. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - class Pattern: NSObject { // MARK: - Properties diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index e3922de..4924dc8 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -14,8 +14,6 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import Foundation - class ReferenceManager { // MARK: - Properties diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 3f5b112..1295d88 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -6,8 +6,6 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import Foundation - class Repository { // MARK: - Properties diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index fb2cec1..4568e1b 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -6,8 +6,6 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - struct Result: Equatable { // MARK: - Properties diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index f815016..f2775b6 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -6,8 +6,6 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - class ResultSet { // MARK: - Properties diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 82da610..4feb191 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -7,8 +7,7 @@ // import XCTest -@testable -import SyntaxKit +@testable import SyntaxKit class ScopedStringTests: XCTestCase { diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 6026f70..926af7a 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -3,13 +3,9 @@ // SyntaxKit // // Created by Sam Soffes on 10/11/14. -// Copyright © 2014-2015 Sam Soffes. -// Copyright (c) Alexander Hedges. -// All rights reserved. +// Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import Foundation - #if os(iOS) || os(watchOS) import UIKit #else From e4a1daf26aa6e7fab9cac8f1b893940a0dae573d Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 12 Jul 2016 21:18:49 +0200 Subject: [PATCH 054/110] start fleshing out comments --- SyntaxKit.xcodeproj/project.pbxproj | 16 ++++++++-------- ...on.swift => AttributedParsingOperation.swift} | 1 + SyntaxKit/Capture.swift | 2 ++ SyntaxKit/CaptureCollection.swift | 2 ++ SyntaxKit/Language.swift | 6 +++++- SyntaxKit/Parser.swift | 13 +++++-------- SyntaxKit/Pattern.swift | 4 ++-- SyntaxKit/Repository.swift | 3 +++ SyntaxKit/ResultSet.swift | 6 +++++- SyntaxKit/ScopedString.swift | 13 +++++++------ SyntaxKit/Tests/ThemeTests.swift | 1 + SyntaxKit/Theme.swift | 5 ++++- 12 files changed, 45 insertions(+), 27 deletions(-) rename SyntaxKit/{ParsingOperation.swift => AttributedParsingOperation.swift} (99%) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 7942117..fdaedcc 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -55,9 +55,9 @@ 2198CEDB1B36D5DE00BD463F /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 2198CEDC1B36D5E100BD463F /* SyntaxKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 211989951B2EC38B00F0D786 /* SyntaxKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; - 8C10B6441CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; - 8C10B6451CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; - 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */; }; + 8C10B6441CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */; }; + 8C10B6451CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */; }; + 8C10B6461CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */; }; 8C10B6471CC3937F00740E00 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; 8C10B6481CC3938100740E00 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C08C3C61C36FD6D00D8548F /* Color.swift */; }; 8C10B6491CC393E700740E00 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -156,7 +156,7 @@ 2122A6E91B22B9320006409B /* SyntaxKitTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SyntaxKitTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; - 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ParsingOperation.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AttributedParsingOperation.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -283,7 +283,7 @@ isa = PBXGroup; children = ( 211989951B2EC38B00F0D786 /* SyntaxKit.h */, - 8C10B6431CC38E5200740E00 /* ParsingOperation.swift */, + 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */, 2119898C1B2EC38B00F0D786 /* AttributedParser.swift */, 211989911B2EC38B00F0D786 /* Parser.swift */, 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */, @@ -536,7 +536,7 @@ 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, - 8C10B6451CC38E5200740E00 /* ParsingOperation.swift in Sources */, + 8C10B6451CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, 8CDD6F1C1C71594B0063915A /* BundleManager.swift in Sources */, 211989C51B2EC40500F0D786 /* Pattern.swift in Sources */, 211989C81B2EC40500F0D786 /* Theme.swift in Sources */, @@ -570,7 +570,7 @@ 8CDD6F1B1C71594B0063915A /* BundleManager.swift in Sources */, 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */, 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */, - 8C10B6441CC38E5200740E00 /* ParsingOperation.swift in Sources */, + 8C10B6441CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */, 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, @@ -610,7 +610,7 @@ 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, - 8C10B6461CC38E5200740E00 /* ParsingOperation.swift in Sources */, + 8C10B6461CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, 8C10B6481CC3938100740E00 /* Color.swift in Sources */, diff --git a/SyntaxKit/ParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift similarity index 99% rename from SyntaxKit/ParsingOperation.swift rename to SyntaxKit/AttributedParsingOperation.swift index a60879b..ffc1e8d 100644 --- a/SyntaxKit/ParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -77,6 +77,7 @@ public class AttributedParsingOperation: NSOperation { super.cancel() } + // MARK: - Change Processing // Algorithmic notes: diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index 4ad7635..722aadb 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -2,6 +2,8 @@ // Capture.swift // SyntaxKit // +// Represents a capture in a TextMate grammar. +// // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index b1bb05b..f259b3b 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -2,6 +2,8 @@ // CaptureCollection.swift // SyntaxKit // +// Represents the captures attribute in a TextMate grammar. +// // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index d25827e..853c27a 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -2,6 +2,9 @@ // Language.swift // SyntaxKit // +// Represents a textmate syntax file (.tmLanguage). Before use the +// validateWithHelperLanguages method has to be called on it. +// // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -10,7 +13,7 @@ public struct Language { // MARK: - Properties - public let UUID: String + public let UUID: String // TODO: replace with uuid type in swift 3 public let name: String public let scopeName: String @@ -20,6 +23,7 @@ public struct Language { static let globalScope = "GLOBAL" + // MARK: - Initializers init?(dictionary: [NSObject: AnyObject]) { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 82b8996..c2709b9 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -3,13 +3,10 @@ // SyntaxKit // // This class is in charge of the painful task of recognizing the syntax -// patterns. It tries to match the output of TextMate as closely as possible. -// Turns out TextMate doesn't highlight things it should highlight according to -// the grammar so this is not entirely straight forward. +// patterns. It tries to match parsing behavior of TextMate as closely as +// possible. // -// It supports incremental parsing. The recommmend usage is to ask the class -// for the range it should be reparsed on the given change. This range can then -// be passed to parsed. +// The parsed string is stored as a property. // // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. @@ -30,6 +27,7 @@ public class Parser { var string: String + // MARK: - Initializers public init(language: Language) { @@ -359,8 +357,7 @@ public class Parser { /// /// - parameter results: The results of the parsing pass /// - parameter scopesString: The place to store the scopes - /// - parameter callback: The method to call on every successful - /// match + /// - parameter callback: The method to call on every successful match private func applyResults(results: ResultSet, inout storingInScopesString scopesString: ScopedString, callback: Callback) { callback(scope: Language.globalScope, range: results.range) for result in results.results where result.range.length > 0 { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 64e39af..d2632c7 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -2,10 +2,10 @@ // Pattern.swift // SyntaxKit // -// Represents a pattern from a TextMate Language Bundle +// Represents a pattern from a TextMate grammar // // The Include class represents a Pattern that is a reference to another part -// of the Bundle. It is only fully functional as a pattern after it has been +// of the Bundle. It is only usable as a pattern after it has been // resolved via the provided method. // // Created by Sam Soffes on 9/18/14. diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 1295d88..0d92a11 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -2,6 +2,9 @@ // Repository.swift // SyntaxKit // +// Represents a repository dictionary from a TextMate grammar. This class +// supports nested repositories as found in some grammars. +// // Created by Alexander Hedges on 09/01/16. // Copyright © 2016 Alexander Hedges. All rights reserved. // diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index f2775b6..67fa395 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -2,6 +2,8 @@ // ResultSet.swift // SyntaxKit // +// Stores a set of results generated by the parser. +// // Created by Sam Soffes on 10/11/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -11,6 +13,8 @@ class ResultSet { // MARK: - Properties var results: [Result] { return _results } + /// Guaranteed to be larger or equal to the union of all the ranges + /// associated with the contained results. var range: NSRange { return _range } private var _results = [Result]() @@ -31,8 +35,8 @@ class ResultSet { } func addResult(result: Result) { - _results.append(result) extendWithRange(result.range) + _results.append(result) } func addResults(resultSet: ResultSet) { diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 33818eb..53f9bc5 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -3,9 +3,10 @@ // SyntaxKit // // A datastructure that facilitates working with strings that have nested -// scopes associated with them. A scope being a named range that can have an +// scopes associated with them. A scope being a named range that can have an // attribute assciated with it for the callers convenience. -// The ranges can be nested. The datastrucuture could be visualized like this: +// The ranges can be nested. The datastructure could be visualized like this: +// In fact, something like this is returned by the prettyPrint function. // // Top: ---- // ------------- @@ -15,11 +16,11 @@ // // Note: // The bottom-most layer is implicit and is not stored. -// If no layer can hold the inserted scope without intersections a new layer is -// added. +// A new layer is added if no layer can hold the inserted scope without +// creating intersections. // -// The datastructure might be optimized with binary search for insertions at -// the individual levels. +// In the future the datastructure could be optimized by using binary search +// for insertions at the individual levels. // // Created by Alexander Hedges on 29/01/16. // Copyright © 2016 Alexander Hedges. All rights reserved. diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 5426e30..9dfc14e 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -16,6 +16,7 @@ class ThemeTests: XCTestCase { let tomorrow = theme("Tomorrow") let solarized = theme("Solarized") + // MARK: - Tests func testLoading() { diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 926af7a..d3be365 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -2,6 +2,9 @@ // Theme.swift // SyntaxKit // +// Represents a TextMate theme file (.tmTheme). Currently only supports the +// foreground text color attribute on a local scope. +// // Created by Sam Soffes on 10/11/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -18,7 +21,7 @@ public struct Theme { // MARK: - Properties - public let UUID: String + public let UUID: String // TODO: replace with uuid type in swift 3 public let name: String public let attributes: [String: Attributes] From 48cd85ff93fa9c480288b256683e126a62aa1676 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 12 Jul 2016 21:52:41 +0200 Subject: [PATCH 055/110] Commenting on pattern --- SyntaxKit/Pattern.swift | 9 +++++---- SyntaxKit/RefernceManager.swift | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index d2632c7..d3d67c2 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -5,8 +5,8 @@ // Represents a pattern from a TextMate grammar // // The Include class represents a Pattern that is a reference to another part -// of the Bundle. It is only usable as a pattern after it has been -// resolved via the provided method. +// in the same or another grammar. It is only usable as a pattern after it has +// been resolved via the provided method (and has type .resolved). // // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. @@ -119,6 +119,7 @@ class Pattern: NSObject { self.subpatterns = [] } + /// Only to be called from Include. Does not create a usable pattern. override init() { super.init() } @@ -142,7 +143,7 @@ class Include: Pattern { private var _type: referenceType private let repositoryRef: String? private let languageRef: String? - private let associatedRepository: Repository? + private weak var associatedRepository: Repository? // MARK: - Initializers @@ -203,7 +204,7 @@ class Include: Pattern { _type = .resolved } - func resolveInterLanguageReferences(ownLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { + func resolveExternalReference(thisLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { let pattern: Pattern? if type == .toBase { pattern = languages[baseName!]!.pattern diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 4924dc8..8a6e836 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -50,8 +50,8 @@ class ReferenceManager { } for language in languages { let includes = language.referenceManager.includes - for include in includes where include.type == .toBase || include.type == .toForeign || include.type == .toForeignRepository { - include.resolveInterLanguageReferences(language, inLanguages: otherLanguages, baseName: basename) + for include in includes { + include.resolveExternalReference(language, inLanguages: otherLanguages, baseName: basename) } } } From d9e69438c8659c9a9ba1b89c95fceb07ed7c2324 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 12 Jul 2016 21:59:34 +0200 Subject: [PATCH 056/110] Document theme and language --- SyntaxKit/Language.swift | 2 +- SyntaxKit/Theme.swift | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 853c27a..45df656 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -46,7 +46,7 @@ public struct Language { /// Only after a call to this method the Language is fit for general use. /// /// - parameter helperLanguages: The languages that the language has - /// references to resolve agains. This should at least contain the + /// references to resolve against. This should at least contain the /// language itself. mutating func validateWithHelperLanguages(helperLanguages: [Language]) { ReferenceManager.resolveExternalReferencesBetweenLanguages(helperLanguages, basename: self.scopeName) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index d3be365..7e91d85 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -26,19 +26,11 @@ public struct Theme { public let attributes: [String: Attributes] public var backgroundColor: Color { - if let color = attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color { - return color - } else { - return Color.whiteColor() - } + return attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color ?? Color.whiteColor() } public var foregroundColor: Color { - if let color = attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color { - return color - } else { - return Color.blackColor() - } + return attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color ?? Color.blackColor() } From 1b6961d60cd16b37e11c787f0cef20dabe86387f Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Wed, 13 Jul 2016 19:09:04 +0200 Subject: [PATCH 057/110] Document BundleManager --- SyntaxKit/BundleManager.swift | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index a24c07b..d97fa42 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -2,6 +2,12 @@ // BundleManager.swift // SyntaxKit // +// Used to get access to SyntaxKit representations of TextMate bundle files. +// This class is used as a gateway for both internal and external use. +// Alternatively a global instace can be used for convenience. It is +// initialized with a callback that tells the bundle manager where to find the +// files. +// // Created by Alexander Hedges on 15/02/16. // Copyright © 2016 Alexander Hedges. All rights reserved. // @@ -10,11 +16,22 @@ public class BundleManager { // MARK: - Types + /// Given an identifier of a grammar file and the format returns a url to the resource. + /// + /// - parameter identifier: The identifier of the file. Used to map it to + /// the name of the file. + /// - parameter isLanguage: Whether the requested file stores a language + /// (.tmLanguage) + /// - returns: A URL pointing to the resource, if found public typealias BundleLocationCallback = (identifier: String, isLanguage: Bool) -> (NSURL?) // MARK: - Properties + /// You probably want to leave the languageCaching property set to true. + /// + /// - note: Setting it to false will not invalidate or purge the cache. This + /// has to be done separately using clearLanguageCache. public var languageCaching = true public static var defaultManager: BundleManager? @@ -27,6 +44,11 @@ public class BundleManager { // MARK: - Initializers + /// Used to initialize the default manager. Unless this is called the + // defaultManager property will be set to nil. + /// + /// - parameter callback: The callback used to find the location of the + /// textmate files. public class func initializeDefaultManagerWithLocationCallback(callback: BundleLocationCallback) { if defaultManager == nil { defaultManager = BundleManager(callback: callback) @@ -48,7 +70,7 @@ public class BundleManager { } self.dependencies = [] - var language = self.getUnvalidatedLanguageWithIdentifier(identifier)! + var language = self.getRawLanguageWithIdentifier(identifier)! language.validateWithHelperLanguages(self.dependencies) if languageCaching { @@ -74,6 +96,7 @@ public class BundleManager { return newTheme } + /// Clears the language cache. Use if low on memory. public func clearLanguageCache() { self.cachedLanguages = [:] } @@ -81,7 +104,9 @@ public class BundleManager { // MARK: - Internal Interface - func getUnvalidatedLanguageWithIdentifier(identifier: String) -> Language? { + /// - parameter identifier: The identifier of the requested language. + /// - returns: The Language with unresolved extenal references, if found + func getRawLanguageWithIdentifier(identifier: String) -> Language? { let indexOfStoredLanguage = self.dependencies.indexOf{ (lang: Language) in lang.scopeName == identifier } if indexOfStoredLanguage != nil { From 11f1bc0634c43840d0a927517e70a35eda237504 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Wed, 13 Jul 2016 19:25:32 +0200 Subject: [PATCH 058/110] Make ScopedString struct --- SyntaxKit/AttributedParsingOperation.swift | 2 +- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Pattern.swift | 4 ++-- SyntaxKit/Result.swift | 2 ++ SyntaxKit/ScopedString.swift | 18 ++++++------------ 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index ffc1e8d..080661b 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -112,7 +112,7 @@ public class AttributedParsingOperation: NSOperation { return nil } - let potentialNewString = previousScopes.copy() as! ScopedString + var potentialNewString = previousScopes let linesRange: NSRange if insertion { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index c2709b9..0ada5c8 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -77,7 +77,7 @@ public class Parser { func parse(inRange range: NSRange?, withDiff diff: (String?, NSRange)?, inout usingPreviousScopesString scopes: ScopedString, match callback: Callback) { var endScope: Scope? = nil var bounds = range ?? NSRange(location: 0, length: (string as NSString).length) - var scopesString = scopes.copy() as! ScopedString + var scopesString = scopes if range != nil && diff != nil { endScope = scopesString.topLevelScopeAtIndex(bounds.location) if diff!.0 == nil { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index d3d67c2..f110113 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -166,12 +166,12 @@ class Include: Pattern { self._type = .toForeignRepository self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) - BundleManager.defaultManager?.getUnvalidatedLanguageWithIdentifier(languageRef!) + BundleManager.defaultManager?.getRawLanguageWithIdentifier(languageRef!) } else { self._type = .toForeign self.repositoryRef = nil self.languageRef = reference - BundleManager.defaultManager?.getUnvalidatedLanguageWithIdentifier(languageRef!) + BundleManager.defaultManager?.getRawLanguageWithIdentifier(languageRef!) } super.init() _parent = parent diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index 4568e1b..7d43f99 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -2,6 +2,8 @@ // Result.swift // SyntaxKit // +// Represents a match by the parser. +// // Created by Sam Soffes on 10/11/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 53f9bc5..6c72427 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -64,7 +64,7 @@ extension NSRange { typealias Scope = Result -class ScopedString: NSObject, NSCopying { +struct ScopedString { // MARK: - Properties @@ -86,12 +86,6 @@ class ScopedString: NSObject, NSCopying { // MARK: - Interface - func copyWithZone(zone: NSZone) -> AnyObject { - let newScopedString = ScopedString(string: self.underlyingString) - newScopedString.levels = levels - return newScopedString - } - func numberOfScopes() -> Int { var sum = 1 for level in levels { @@ -108,7 +102,7 @@ class ScopedString: NSObject, NSCopying { return index >= 0 && index <= baseScope.range.length } - func addScopeAtBottom(scope: Scope) { + mutating func addScopeAtBottom(scope: Scope) { assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false @@ -124,7 +118,7 @@ class ScopedString: NSObject, NSCopying { } } - func addScopeAtTop(scope: Scope) { + mutating func addScopeAtTop(scope: Scope) { assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false @@ -185,7 +179,7 @@ class ScopedString: NSObject, NSCopying { return -1 } - func removeScopesInRange(range: NSRange) { + mutating func removeScopesInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) for level in (levels.count - 1).stride(through: 0, by: -1) { @@ -201,7 +195,7 @@ class ScopedString: NSObject, NSCopying { } } - func insertString(string: String, atIndex index: Int) { + mutating func insertString(string: String, atIndex index: Int) { assert(index >= 0 && index <= baseScope.range.length) let s = underlyingString as NSString @@ -216,7 +210,7 @@ class ScopedString: NSObject, NSCopying { } } - func deleteCharactersInRange(range: NSRange) { + mutating func deleteCharactersInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString From 9e7049e98dfe29e4878663c58062229966ee3446 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 12:25:11 +0200 Subject: [PATCH 059/110] Allow use of non-global bundle manager --- SyntaxKit/BundleManager.swift | 2 +- SyntaxKit/Language.swift | 5 +++-- SyntaxKit/Pattern.swift | 8 ++++---- SyntaxKit/RefernceManager.swift | 10 +++++++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index d97fa42..95179e8 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -114,7 +114,7 @@ public class BundleManager { } else { guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), plist = NSDictionary(contentsOfURL: dictURL), - newLanguage = Language(dictionary: plist as [NSObject : AnyObject]) else { + newLanguage = Language(dictionary: plist as [NSObject : AnyObject], bundleManager: self) else { return nil } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 45df656..802c6b5 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -18,7 +18,7 @@ public struct Language { public let scopeName: String let pattern: Pattern = Pattern() - let referenceManager = ReferenceManager() + let referenceManager: ReferenceManager let repository: Repository static let globalScope = "GLOBAL" @@ -26,7 +26,7 @@ public struct Language { // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject]) { + init?(dictionary: [NSObject: AnyObject], bundleManager: BundleManager) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, scopeName = dictionary["scopeName"] as? String, @@ -36,6 +36,7 @@ public struct Language { self.UUID = UUID self.name = name self.scopeName = scopeName + self.referenceManager = ReferenceManager(bundleManager) self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, withReferenceManager: referenceManager) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index f110113..4de1569 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -119,7 +119,7 @@ class Pattern: NSObject { self.subpatterns = [] } - /// Only to be called from Include. Does not create a usable pattern. + /// - note: For most cases does not create a usable pattern. override init() { super.init() } @@ -148,7 +148,7 @@ class Include: Pattern { // MARK: - Initializers - init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?) { + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?, bundleManager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { self._type = .toRepository @@ -166,12 +166,12 @@ class Include: Pattern { self._type = .toForeignRepository self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) - BundleManager.defaultManager?.getRawLanguageWithIdentifier(languageRef!) + bundleManager.getRawLanguageWithIdentifier(languageRef!) } else { self._type = .toForeign self.repositoryRef = nil self.languageRef = reference - BundleManager.defaultManager?.getRawLanguageWithIdentifier(languageRef!) + bundleManager.getRawLanguageWithIdentifier(languageRef!) } super.init() _parent = parent diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 8a6e836..cc754ba 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -19,10 +19,18 @@ class ReferenceManager { // MARK: - Properties private var includes: [Include] = [] + private weak var bundleManager: BundleManager? + + + // MARK: - Init + + init(bundleManager: BundleManager) { + self.bundleManager = bundleManager + } // MARK: - Pattern Creation and Resolution - + func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { var results: [Pattern] = [] for rawPattern in patterns { From e3cb7ab5936db1b4250dfb802e9daac1d78d8f5b Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 12:26:03 +0200 Subject: [PATCH 060/110] comment on ScopedString and small name changes --- SyntaxKit/AttributedParsingOperation.swift | 2 +- SyntaxKit/Parser.swift | 8 ++--- SyntaxKit/ScopedString.swift | 39 ++++++++++++++++------ SyntaxKit/Tests/ScopedStringTests.swift | 32 +++++++++--------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index 080661b..fd5df9b 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -126,7 +126,7 @@ public class AttributedParsingOperation: NSOperation { return nil } - let scopeAtIndex = potentialNewString.topLevelScopeAtIndex(NSMaxRange(linesRange) - 1) + let scopeAtIndex = potentialNewString.topmostScopeAtIndex(NSMaxRange(linesRange) - 1) if scopeAtIndex == potentialNewString.baseScope { return linesRange } else { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 0ada5c8..35a779e 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -79,7 +79,7 @@ public class Parser { var bounds = range ?? NSRange(location: 0, length: (string as NSString).length) var scopesString = scopes if range != nil && diff != nil { - endScope = scopesString.topLevelScopeAtIndex(bounds.location) + endScope = scopesString.topmostScopeAtIndex(bounds.location) if diff!.0 == nil { scopesString.deleteCharactersInRange(diff!.1) } else { @@ -118,7 +118,7 @@ public class Parser { } if startIndex > endIndex && scopesString.isInString(startIndex + 1) { - let scopeAtIndex = scopesString.topLevelScopeAtIndex(startIndex + 1) + let scopeAtIndex = scopesString.topmostScopeAtIndex(startIndex + 1) if endScope == nil && scopesString.levelForScope(scopeAtIndex) > 0 || endScope != nil && scopesString.levelForScope(scopeAtIndex) > scopesString.levelForScope(endScope!) { endIndex = NSMaxRange(scopeAtIndex.range) @@ -348,10 +348,6 @@ public class Parser { return resultSet } - // Implementation note: - // In this project the difference between a Result and a Scope is that - // the scope has the attribute set while the Result does not. - /// Uses the callback to communicate the result of the parsing pass back /// to the caller of parse. /// diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 6c72427..aeadc9f 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -15,7 +15,8 @@ // String: "(This is) (string (with (nest)ed) scopes)!" // // Note: -// The bottom-most layer is implicit and is not stored. +// In the picture above the parens are not actually in the string, they serve +// visualization purposes. The bottom-most layer is implicit and is not stored. // A new layer is added if no layer can hold the inserted scope without // creating intersections. // @@ -46,14 +47,17 @@ extension NSRange { return location <= otherRange.location && location + length >= otherRange.location + otherRange.length } - mutating func subtractRange(range: NSRange) { + /// Removes the indexes contained in range from self and shifts itself as + /// needed to not leave a gap in the domain. + mutating func removeIndexesFromRange(range: NSRange) { length -= NSIntersectionRange(range, NSRange(location: location, length: length)).length if (range.location < self.location) { self.location -= NSIntersectionRange(range, NSRange(location: 0, length: self.location)).length } } - mutating func insertRange(range: NSRange) { + /// Inserts the indexes contained in range into self. Grows as needed. + mutating func insertIndexesFromRange(range: NSRange) { if self.containsIndex(range.location) && range.location < NSMaxRange(self) { length += range.length } else if location > range.location { @@ -62,6 +66,9 @@ extension NSRange { } } +/// In this project the difference between a Result and a Scope is that the +/// scope has the attribute set while the Result does not. This is an implicit +/// agreement, please respect. typealias Scope = Result struct ScopedString { @@ -134,7 +141,7 @@ struct ScopedString { } } - func topLevelScopeAtIndex(index: Int) -> Scope { + func topmostScopeAtIndex(index: Int) -> Scope { let indexRange = NSRange(location: index, length: 0) for i in (levels.count - 1).stride(through: 0, by: -1) { let level = levels[i] @@ -179,6 +186,7 @@ struct ScopedString { return -1 } + /// Removes all scopes that are entirely contained in the spcified range. mutating func removeScopesInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) @@ -195,6 +203,9 @@ struct ScopedString { } } + /// Inserts the given string into the underlying string, stretching and + /// shifting ranges as needed. If the range starts before and ends after the + /// insertion point, it is stretched. mutating func insertString(string: String, atIndex index: Int) { assert(index >= 0 && index <= baseScope.range.length) @@ -205,11 +216,13 @@ struct ScopedString { self.underlyingString = mutableString.copy() as! String for level in 0.. String { + var result = "" var printableUnderlyingString = underlyingString.stringByReplacingOccurrencesOfString("\n", withString: "¬") printableUnderlyingString = printableUnderlyingString.stringByReplacingOccurrencesOfString("\t", withString: "»") - print(printableUnderlyingString) + result += printableUnderlyingString + "\n" for level in (levels.count - 1).stride(through: 0, by: -1) { var levelString = String(count: (underlyingString as NSString).length, repeatedValue: " " as Character) for pattern in levels[level] { @@ -249,7 +267,7 @@ struct ScopedString { levelString = (levelString as NSString).stringByReplacingCharactersInRange(range, withString: "[\(dashes)]") } } - print(levelString) + result += levelString + "\n" } var numberString = "" for i in 0...(underlyingString as NSString).length/10 { @@ -257,7 +275,8 @@ struct ScopedString { let dashes = String(count: 9 - numDigits, repeatedValue: "-" as Character) numberString += "\(i*10)\(dashes)|" } - print(numberString) + result += numberString + "\n" + return result } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 4feb191..b01588f 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -20,19 +20,19 @@ class ScopedStringTests: XCTestCase { } func testScopesString() { - let newScopedString = ScopedString(string: "Test") + var newScopedString = ScopedString(string: "Test") XCTAssert(newScopedString.numberOfScopes() == 1) XCTAssert(newScopedString.numberOfLevels() == 1) - XCTAssert(newScopedString.topLevelScopeAtIndex(2) == newScopedString.baseScope) + XCTAssert(newScopedString.topmostScopeAtIndex(2) == newScopedString.baseScope) let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) newScopedString.addScopeAtTop(newScope1) XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) - XCTAssert(newScopedString.topLevelScopeAtIndex(0) == newScopedString.baseScope) - XCTAssert(newScopedString.topLevelScopeAtIndex(1) == newScope1) + XCTAssert(newScopedString.topmostScopeAtIndex(0) == newScopedString.baseScope) + XCTAssert(newScopedString.topmostScopeAtIndex(1) == newScope1) XCTAssert(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1) == newScopedString.baseScope) let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) @@ -40,8 +40,8 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.numberOfScopes() == 3) XCTAssert(newScopedString.numberOfLevels() == 3) - XCTAssert(newScopedString.topLevelScopeAtIndex(1) == newScope1) - XCTAssert(newScopedString.topLevelScopeAtIndex(2) == newScope2) + XCTAssert(newScopedString.topmostScopeAtIndex(1) == newScope1) + XCTAssert(newScopedString.topmostScopeAtIndex(2) == newScope2) XCTAssertFalse(newScopedString.numberOfScopes() == 1) @@ -50,7 +50,7 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) - XCTAssert(newScopedString.topLevelScopeAtIndex(1).range == NSRange(location: 1, length: 2)) + XCTAssert(newScopedString.topmostScopeAtIndex(1).range == NSRange(location: 1, length: 2)) newScopedString.insertString("ssssss", atIndex: 2) XCTAssert(newScopedString.underlyingString == "Tesssssst") @@ -61,12 +61,12 @@ class ScopedStringTests: XCTestCase { let newScope4 = Scope(identifier: "zeroLengthScope2", range: NSRange(location: 3, length: 0), attribute: nil) newScopedString.addScopeAtTop(newScope4) XCTAssert(newScopedString.numberOfScopes() == 4) - XCTAssert(newScopedString.topLevelScopeAtIndex(0).patternIdentifier == "zeroLengthScope1") + XCTAssert(newScopedString.topmostScopeAtIndex(0).patternIdentifier == "zeroLengthScope1") newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) XCTAssert(newScopedString.numberOfScopes() == 3) - XCTAssert(newScopedString.topLevelScopeAtIndex(2).range == NSRange(location: 1, length: 8)) + XCTAssert(newScopedString.topmostScopeAtIndex(2).range == NSRange(location: 1, length: 8)) } func testRangeExtension() { @@ -82,31 +82,31 @@ class ScopedStringTests: XCTestCase { XCTAssertFalse(someRange.containsIndex(23)) someRange = NSRange(location: 0, length: 24) - someRange.subtractRange(NSRange(location: 2, length: 4)) + someRange.removeIndexesFromRange(NSRange(location: 2, length: 4)) XCTAssertEqual(someRange, NSRange(location: 0, length: 20)) someRange = NSRange(location: 20, length: 40) - someRange.subtractRange(NSRange(location: 4, length: 12)) + someRange.removeIndexesFromRange(NSRange(location: 4, length: 12)) XCTAssertEqual(someRange, NSRange(location: 8, length: 40)) someRange = NSRange(location: 23, length: 11) - someRange.subtractRange(NSRange(location: 20, length: 5)) + someRange.removeIndexesFromRange(NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 20, length: 9)) someRange = NSRange(location: 10, length: 14) - someRange.subtractRange(NSRange(location: 5, length: 40)) + someRange.removeIndexesFromRange(NSRange(location: 5, length: 40)) XCTAssertTrue(someRange.isEmpty()) someRange = NSRange(location: 23, length: 11) - someRange.insertRange(NSRange(location: 20, length: 5)) + someRange.insertIndexesFromRange(NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 28, length: 11)) someRange = NSRange(location: 14, length: 2) - someRange.insertRange(NSRange(location: 15, length: 7)) + someRange.insertIndexesFromRange(NSRange(location: 15, length: 7)) XCTAssertEqual(someRange, NSRange(location: 14, length: 9)) someRange = NSRange(location: 26, length: 36) - someRange.insertRange(NSRange(location: 62, length: 5)) + someRange.insertIndexesFromRange(NSRange(location: 62, length: 5)) XCTAssertEqual(someRange, NSRange(location: 26, length: 36)) } } From a6c8c62edf1f9810102526620e0aca7a9c08fa5f Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 12:46:21 +0200 Subject: [PATCH 061/110] Update readme and small fixes --- Readme.markdown | 16 ++++++++-------- SyntaxKit/BundleManager.swift | 2 +- SyntaxKit/Language.swift | 2 +- SyntaxKit/Pattern.swift | 2 +- SyntaxKit/RefernceManager.swift | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index c4c9fa7..b0ce578 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -4,12 +4,12 @@ SyntaxKit makes TextMate-style syntax highlighting easy. It works on iOS, watchOS, and OS X. -A fork from Sam Soffes' SyntaxKit which was originally abstracted from [Whiskey](http://usewhiskey.com). +SyntaxKit was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building -SyntaxKit is written in Swift 2 so Xcode 7 is required. There aren't any dependencies besides system frameworks. +SyntaxKit is written in Swift 3 so Xcode 8 is required. There aren't any dependencies besides system frameworks. ## Installation @@ -35,20 +35,20 @@ SyntaxKit uses `tmLanguage` and `tmTheme` files to highlight source code. None a ### Basic Parsing -Once you have a language, you can get started: +Once you have a BundleManager, you can get started: ```swift import SyntaxKit -BundleManager.initializeDefaultManagerWithLocationCallback { identifier, isLanguage in +let manager = BundleManager() { identifier, isLanguage in NSURL(string: "Location of Bundles/" + identifier + ".plist") } -let yaml = BundleManager.defaultManager!.languageWithIdentifier("source.yaml")! +let yaml = manager.languageWithIdentifier("source.yaml")! let parser = Parser(language: yaml) ``` -`Parser` is a very simple class that just calls a block when it finds something the language file knows about. Let's print all of the elements in this string: +`Parser` is a class that calls a block when it finds something the language file knows about. Let's print all of the elements in this string: ```swift let input = "title: \"Hello World\"\n" @@ -65,7 +65,7 @@ parser.parse(input) { scope, range in SyntaxKit also comes with `AttributedParser`. This is a simple subclass of `Parser` that knows how to work with themes. ```swift -let tomorrow = BundleManager.defaultManager!.themeWithIdentifier("tomorrow")! +let tomorrow = manager.themeWithIdentifier("tomorrow")! let attributedParser = AttributedParser(language: yaml, theme: tomorrow) attributedParser.parse(input) { scope, range, attributes in @@ -90,7 +90,7 @@ If you want to build your own parser (for example, to generate HTML) you can sub Enjoy. -## A list of Caveats +## Caveats There are however a few things you got to watch out for: diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 95179e8..d3a3883 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -57,7 +57,7 @@ public class BundleManager { } } - init(callback: BundleLocationCallback) { + public init(callback: BundleLocationCallback) { self.bundleCallback = callback } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 802c6b5..c8fcc29 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -36,7 +36,7 @@ public struct Language { self.UUID = UUID self.name = name self.scopeName = scopeName - self.referenceManager = ReferenceManager(bundleManager) + self.referenceManager = ReferenceManager(bundleManager: bundleManager) self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, withReferenceManager: referenceManager) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 4de1569..2fb70a7 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -119,7 +119,7 @@ class Pattern: NSObject { self.subpatterns = [] } - /// - note: For most cases does not create a usable pattern. + /// For most cases does not create a usable pattern. override init() { super.init() } diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index cc754ba..86aac69 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -35,7 +35,7 @@ class ReferenceManager { var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { - let reference = Include(reference: include, inRepository: repository, parent: caller) + let reference = Include(reference: include, inRepository: repository, parent: caller, bundleManager: bundleManager!) self.includes.append(reference) results.append(reference) } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, withRepository: repository, withReferenceManager: self) { From cbbe9f7cb90a18f2ad2956d387cba7d4c610a7f3 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 13:14:07 +0200 Subject: [PATCH 062/110] closes #2 --- SyntaxKit/Parser.swift | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 35a779e..0e0d12b 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -166,25 +166,19 @@ public class Parser { var lineEnd = startIndex let result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) - var bestMatchForMiddle: (pattern: Pattern, start: Int)? - var endMatchResult: ResultSet? - var resultForMiddle: ResultSet? - var range: NSRange - var newStart: Int - while lineEnd < stop { (string as NSString).getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) - range = NSRange(location: lineStart, length: lineEnd - lineStart) + var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { if aborted { return nil } - bestMatchForMiddle = findBestPatternInPatterns(patterns, inRange: range) + let bestMatchForMiddle = findBestPatternInPatterns(patterns, inRange: range) if endPattern != nil { - endMatchResult = self.matchExpression(endPattern!.end!, inRange: range, captures: endPattern!.endCaptures) + let endMatchResult = self.matchExpression(endPattern!.end!, inRange: range, captures: endPattern!.endCaptures) if endMatchResult != nil && (bestMatchForMiddle == nil || bestMatchForMiddle != nil && (!endPattern!.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.start || endMatchResult!.range.location < bestMatchForMiddle!.start)) { result.addResults(endMatchResult!) @@ -193,12 +187,12 @@ public class Parser { } if bestMatchForMiddle != nil { - resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inRange: range) + let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inRange: range) if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } result.addResults(resultForMiddle!) - newStart = NSMaxRange(resultForMiddle!.range) + let newStart = NSMaxRange(resultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) lineEnd = max(lineEnd, newStart) } else { From ee28caeb85ddcc3b0074eaf21d8d735c61e1d4f0 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 17:23:29 +0200 Subject: [PATCH 063/110] make logical types more encapsulated --- SyntaxKit/AttributedParser.swift | 4 +- SyntaxKit/AttributedParsingOperation.swift | 100 +++++++++++++-------- SyntaxKit/Parser.swift | 54 +++++------ 3 files changed, 91 insertions(+), 67 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 2f92c43..6b1fd53 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -36,8 +36,8 @@ public class AttributedParser: Parser { } } - func parse(inRange range: NSRange?, withDiff diff: (String?, NSRange)?, inout usingPreviousScopesString scopes: ScopedString, match callback: AttributedCallback) { - parse(inRange: range, withDiff: diff, usingPreviousScopesString: &scopes) { scope, range in + func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: AttributedCallback) -> ScopedString? { + return parse(incremental) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index fd5df9b..c49228e 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -2,10 +2,53 @@ // AttributedParsingOperation.swift // SyntaxKit // +// Subclass of NSOperation that can be used for mutithreaded incremental +// parsing with all the benefits of NSOperationQueue. +// // Created by Alexander Hedges on 17/04/16. // Copyright © 2016 Alexander Hedges. All rights reserved. // +struct Diff { + + // MARK: - Properties + + var change: String + var range: NSRange + + // MARK: - Operations + + /// - returns: true if predicted change from diff matches the characters + /// from the new string in that range + func representsChangesfromOldString(oldString: NSString, toNewString newStr: NSString) -> Bool { + if self.range.length == 0 { + if !Diff.stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: self.range.location, length: (self.change as NSString).length), oldString: oldString) { + return false + } + if newStr.substringWithRange(NSRange(location: self.range.location, length: (self.change as NSString).length)) != self.change { + return false + } + } else { + if !Diff.stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: self.range, oldString: oldString) { + return false + } + } + + return true + } + + /// - returns: true if the number of characters changed is consistent with + /// the new string + private static func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange, oldString: NSString) -> Bool { + let oldLength = insertion ? newString.length - range.length : newString.length + range.length + + if oldString.length != oldLength { + return false + } + return true + } +} + public class AttributedParsingOperation: NSOperation { // MARK: - Types @@ -21,7 +64,7 @@ public class AttributedParsingOperation: NSOperation { private var scopedStringResult: ScopedString private var range: NSRange? - private var diff: (String?, NSRange)? + private var diff: Diff? // MARK: - Initializers @@ -44,14 +87,14 @@ public class AttributedParsingOperation: NSOperation { super.init() let s = string as NSString - let diff: (String?, NSRange) + let diff: Diff if insertion { - diff = (s.substringWithRange(range), NSRange(location: range.location, length: 0)) + diff = Diff(change: s.substringWithRange(range), range: NSRange(location: range.location, length: 0)) } else { - diff = (nil, range) + diff = Diff(change: "", range: range) } - if diffRepresentsChanges(diff, fromOldString: previousOperation.scopedStringResult.underlyingString, toNewString: string) { + if diff.representsChangesfromOldString(previousOperation.scopedStringResult.underlyingString, toNewString: string) { self.diff = diff self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range, previousScopedString: scopedStringResult) } @@ -63,11 +106,22 @@ public class AttributedParsingOperation: NSOperation { public override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] parser.string = stringToParse - parser.parse(inRange: range, withDiff: diff, usingPreviousScopesString: &scopedStringResult) { _, range, attributes in + + var incrementalParsingInfo: (NSRange, Diff, ScopedString)? + if range != nil && diff != nil { + incrementalParsingInfo = (range: range!, diff: diff!, previousScopes: scopedStringResult) + } + + let callback = { (_: String, range: NSRange, attributes: Attributes?) in if let attributes = attributes { resultsArray.append((range, attributes)) } } + + if let result = parser.parse(incrementalParsingInfo, match: callback) { + scopedStringResult = result + } + operationCallback(resultsArray) parser.string = "" } @@ -80,7 +134,7 @@ public class AttributedParsingOperation: NSOperation { // MARK: - Change Processing - // Algorithmic notes: + // Implementation notes: // If change occurred in a block reparse the lines in which the change // happened and the range of the block from this point on. If the change // occurred in the global scope just reparse the lines that changed. @@ -108,7 +162,7 @@ public class AttributedParsingOperation: NSOperation { /// - returns: A range in newString that can be safely re-parsed. Or nil if /// everything has to be reparsed. func outdatedRangeForChangeInString(newString: NSString, changeIsInsertion insertion: Bool, changedRange range: NSRange, previousScopedString previousScopes: ScopedString) -> NSRange? { - if !stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: previousScopes.underlyingString) { + if !Diff.stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: previousScopes.underlyingString) { return nil } @@ -134,34 +188,4 @@ public class AttributedParsingOperation: NSOperation { return NSUnionRange(linesRange, NSRange(location: range.location, length: endOfCurrentScope - range.location)) } } - - /// - returns: true if diff not nil and predicted change from diff matches - /// the characters from the new string in that range - func diffRepresentsChanges(diff: (String?, NSRange), fromOldString oldString: NSString, toNewString newStr: NSString) -> Bool { - if diff.0 == nil { - if !stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: diff.1, oldString: oldString) { - return false - } - } else { - if !stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: diff.1.location, length: (diff.0! as NSString).length), oldString: oldString) { - return false - } - if newStr.substringWithRange(NSRange(location: diff.1.location, length: (diff.0! as NSString).length)) != diff.0! { - return false - } - } - - return true - } - - /// - returns: true if scopeString not nil and the number of characters - /// changed is consistent with the new string - func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange, oldString: NSString) -> Bool { - let oldLength = insertion ? newString.length - range.length : newString.length + range.length - - if oldString.length != oldLength { - return false - } - return true - } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 0e0d12b..f1cafad 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -3,7 +3,7 @@ // SyntaxKit // // This class is in charge of the painful task of recognizing the syntax -// patterns. It tries to match parsing behavior of TextMate as closely as +// patterns. It tries to match parsing behavior of TextMate as closely as // possible. // // The parsed string is stored as a property. @@ -43,8 +43,7 @@ public class Parser { return } self.string = string - var scope = ScopedString(string: string) - parse(inRange: nil, withDiff: nil, usingPreviousScopesString: &scope, match: callback) + parse(match: callback) self.string = "" } @@ -68,29 +67,28 @@ public class Parser { /// - parameter range: The range in which the string should be parsed. /// On nil the entire string will be parsed. /// - parameter diff: Addition: "Added string", (insertionIndex, 0) - /// Deletion: nil, (deletionStart, deletionLength) + /// Deletion: "", (deletionStart, deletionLength) /// - parameter scopes: Denotes /// - parameter callback: The callback to call on every match of a /// pattern identifier of the language /// - returns: A scopedString that contains the range results of the parsing - /// Or nil if the parsing was aborted. - func parse(inRange range: NSRange?, withDiff diff: (String?, NSRange)?, inout usingPreviousScopesString scopes: ScopedString, match callback: Callback) { - var endScope: Scope? = nil - var bounds = range ?? NSRange(location: 0, length: (string as NSString).length) - var scopesString = scopes - if range != nil && diff != nil { + /// Or nil if the parsing was aborted. + func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: Callback) -> ScopedString? { + let bounds: NSRange + var scopesString: ScopedString + var endScope: Scope? + if incremental != nil && incremental!.previousScopes.underlyingString != (string as NSString).stringByReplacingCharactersInRange(incremental!.diff.range, withString: incremental!.diff.change) { + bounds = incremental!.range + scopesString = incremental!.previousScopes endScope = scopesString.topmostScopeAtIndex(bounds.location) - if diff!.0 == nil { - scopesString.deleteCharactersInRange(diff!.1) + if incremental!.diff.range.length == 0 { + scopesString.insertString(incremental!.diff.change, atIndex: incremental!.diff.range.location) } else { - scopesString.insertString(diff!.0!, atIndex: diff!.1.location) - } - if scopesString.underlyingString != string { // recover from inconsistecy (for instance "." shortcut) - print("Used the emergency trick") - bounds = NSRange(location: 0, length: (string as NSString).length) - endScope = nil - scopesString = ScopedString(string: string) + scopesString.deleteCharactersInRange(incremental!.diff.range) } + } else { + bounds = NSRange(location: 0, length: (string as NSString).length) + scopesString = ScopedString(string: string) } var startIndex = bounds.location @@ -100,7 +98,7 @@ public class Parser { while startIndex < endIndex { let endPattern = endScope?.attribute as! Pattern? guard let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) else { - return + return nil } if endScope != nil { @@ -125,11 +123,13 @@ public class Parser { } } } - scopesString.removeScopesInRange(allResults.range) - scopes = scopesString - if !aborted { - self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) + + if aborted { + return nil } + scopesString.removeScopesInRange(allResults.range) + self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) + return scopesString } // Algorithmic notes: @@ -175,7 +175,7 @@ public class Parser { return nil } - let bestMatchForMiddle = findBestPatternInPatterns(patterns, inRange: range) + let bestMatchForMiddle = findMatchFromPatterns(patterns, inRange: range) if endPattern != nil { let endMatchResult = self.matchExpression(endPattern!.end!, inRange: range, captures: endPattern!.endCaptures) @@ -223,7 +223,7 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findBestPatternInPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { + private func findMatchFromPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { var interestingBounds = bounds var bestResult: (pattern: Pattern, start: Int)? for pattern in patterns { @@ -250,7 +250,7 @@ public class Parser { return (pattern, beginResults.range.location) } } else if pattern.subpatterns.count >= 1 { - return findBestPatternInPatterns(pattern.subpatterns, inRange: bounds) + return findMatchFromPatterns(pattern.subpatterns, inRange: bounds) } return nil } From af90ce6be56ab8809e44034df81a9ed6a62813f2 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 18:17:06 +0200 Subject: [PATCH 064/110] closes #5 Reuse the already matched results --- SyntaxKit/AttributedParsingOperation.swift | 8 +- SyntaxKit/BundleManager.swift | 2 +- SyntaxKit/Parser.swift | 115 +++++++++------------ 3 files changed, 55 insertions(+), 70 deletions(-) diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index c49228e..a416043 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -134,10 +134,10 @@ public class AttributedParsingOperation: NSOperation { // MARK: - Change Processing - // Implementation notes: - // If change occurred in a block reparse the lines in which the change - // happened and the range of the block from this point on. If the change - // occurred in the global scope just reparse the lines that changed. + // Implementation notes: + // If change occurred in a block reparse the lines in which the change + // happened and the range of the block from this point on. If the change + // occurred in the global scope just reparse the lines that changed. /// Returns the range in the given string that should be re-parsed after the /// given change. diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index d3a3883..ee1d505 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -45,7 +45,7 @@ public class BundleManager { // MARK: - Initializers /// Used to initialize the default manager. Unless this is called the - // defaultManager property will be set to nil. + /// defaultManager property will be set to nil. /// /// - parameter callback: The callback used to find the location of the /// textmate files. diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index f1cafad..43ccaf0 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -50,11 +50,11 @@ public class Parser { // MARK: - Private - // Implementation notes: - // The first part tries to find the context in which parsing should take - // place (which block we are in), if any. - // The second part parses the string the until the full range is consumed - // and it may exceed that range if further parts of the string are outdated + // Implementation notes: + // The first part tries to find the context in which parsing should take + // place (which block we are in), if any. + // The second part parses the string the until the full range is consumed + // and it may exceed that range if further parts of the string are outdated /// Parses the given string in the given range and calls the callback on /// every match of a scope @@ -97,7 +97,7 @@ public class Parser { while startIndex < endIndex { let endPattern = endScope?.attribute as! Pattern? - guard let results = self.matchPatterns(endPattern?.subpatterns ?? language.pattern.subpatterns, withEndPatternFromPattern: endPattern, startingAtIndex: startIndex, stopIndex: endIndex) else { + guard let results = self.matchSubpatternsOfPattern(endPattern ?? language.pattern, inRange: NSRange(location: startIndex, length: endIndex - startIndex)) else { return nil } @@ -132,39 +132,32 @@ public class Parser { return scopesString } - // Algorithmic notes: - // A pattern expression can not match a substring spanning multiple lines - // so in the outer loop the string is decomposed into its lines. - // In the inner loop it tries to repeatedly match a pattern followed by the - // end pattern until either the line is consumed or it has found the end. - // This procedure is repeated with the subsequent lines until it has either - // matched the end pattern or the string is consumed entirely. - // If it can find neither in a line it moves to the next one. + // Algorithmic notes: + // A pattern expression can not match a substring spanning multiple lines + // so in the outer loop the string is decomposed into its lines. + // In the inner loop it tries to repeatedly match a pattern followed by the + // end pattern until either the line is consumed or it has found the end. + // This procedure is repeated with the subsequent lines until it has either + // matched the end pattern or the string is consumed entirely. + // If it can find neither in a line it moves to the next one. - // Implementation note: - // The matching of the middle part may return a match that goes beyond the - // given range. This is intentional. + // Implementation note: + // The matching of the middle part may return a match that goes beyond the + // given range. This is intentional. - /// Matches an array of patterns in the input + /// Matches subpatterns of the given pattern in the input. /// - /// - parameter patterns: The patterns that should be matched - /// - parameter string: The string that should be matched against - /// - parameter endPattern: If specified, the pattern at which to stop - /// the matching process, overrides stopIndex. - /// On nil it will match up to stopIndex. - /// - parameter startingIndex: The index at which to start matching - /// - parameter stopIndex: The index at which to stop matching + /// - parameter pattern: The patterns whose subpatterns should be matched + /// - parameter bounds: The range in which the matching should occur. /// /// - returns: The result set containing the lexical scope names with range - /// information. May exceed stopIndex. - /// Only returns nil if the operation was aborted. - private func matchPatterns(patterns: [Pattern], withEndPatternFromPattern endPattern: Pattern?, startingAtIndex startIndex: Int, stopIndex stop: Int) -> ResultSet? { - assert(endPattern == nil || endPattern!.end != nil) - + /// information or nil if aborted. May exceed range. + private func matchSubpatternsOfPattern(pattern: Pattern, inRange bounds: NSRange) -> ResultSet? { + let stop = bounds.location + bounds.length assert((string as NSString).length >= stop) - var lineStart = startIndex - var lineEnd = startIndex - let result = ResultSet(startingRange: NSRange(location: startIndex, length: 0)) + var lineStart = bounds.location + var lineEnd = bounds.location + let result = ResultSet(startingRange: NSRange(location: bounds.location, length: 0)) while lineEnd < stop { (string as NSString).getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSMakeRange(lineEnd, 0)) @@ -175,19 +168,24 @@ public class Parser { return nil } - let bestMatchForMiddle = findMatchFromPatterns(patterns, inRange: range) + let bestMatchForMiddle = findMatchFromPatterns(pattern.subpatterns, inRange: range) - if endPattern != nil { - let endMatchResult = self.matchExpression(endPattern!.end!, inRange: range, captures: endPattern!.endCaptures) + if pattern.end != nil { + let endMatchResult = self.matchExpression(pattern.end!, inRange: range, captures: pattern.endCaptures) if endMatchResult != nil && (bestMatchForMiddle == nil || bestMatchForMiddle != nil && - (!endPattern!.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.start || endMatchResult!.range.location < bestMatchForMiddle!.start)) { + (!pattern.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.match.range.location || endMatchResult!.range.location < bestMatchForMiddle!.match.range.location)) { result.addResults(endMatchResult!) return result } } if bestMatchForMiddle != nil { - let resultForMiddle = matchPattern(bestMatchForMiddle!.pattern, inRange: range) + let resultForMiddle: ResultSet? + if bestMatchForMiddle!.pattern.match != nil { + resultForMiddle = bestMatchForMiddle!.match + } else { + resultForMiddle = matchAfterBeginOfPattern(bestMatchForMiddle!.pattern, beginResults: bestMatchForMiddle!.match, inRange: range) + } if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } @@ -203,7 +201,7 @@ public class Parser { lineStart = lineEnd } - result.extendWithRange(NSRange(location: startIndex, length: stop - startIndex)) + result.extendWithRange(bounds) return result } @@ -223,31 +221,31 @@ public class Parser { /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any /// information associated with the match. - private func findMatchFromPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { + private func findMatchFromPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { var interestingBounds = bounds - var bestResult: (pattern: Pattern, start: Int)? + var bestResult: (pattern: Pattern, match: ResultSet)? for pattern in patterns { let currentMatch = self.firstMatchOfPattern(pattern, inRange: bounds) - if currentMatch?.start == bounds.location { + if currentMatch?.match.range.location == bounds.location { return currentMatch - } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.start < bestResult!.start) { + } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.match.range.location < bestResult!.match.range.location) { bestResult = currentMatch - interestingBounds.length = currentMatch!.start - interestingBounds.location + interestingBounds.length = currentMatch!.match.range.location - interestingBounds.location } } return bestResult } - private func firstMatchOfPattern(pattern: Pattern, inRange bounds: NSRange) -> (pattern: Pattern, start: Int)? { + private func firstMatchOfPattern(pattern: Pattern, inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { if let match = pattern.match { if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { - return (pattern, resultSet.range.location) + return (pattern, resultSet) } } } else if let begin = pattern.begin { if let beginResults = matchExpression(begin, inRange: bounds, captures: pattern.beginCaptures) { - return (pattern, beginResults.range.location) + return (pattern, beginResults) } } else if pattern.subpatterns.count >= 1 { return findMatchFromPatterns(pattern.subpatterns, inRange: bounds) @@ -255,9 +253,9 @@ public class Parser { return nil } - // Implementation note: - // The order in which the beginning middle and end are added to the final - // result matters. + // Implementation note: + // The order in which the beginning middle and end are added to the final + // result matters. /// Matches a single pattern in the string in the given range /// @@ -269,20 +267,9 @@ public class Parser { /// matched successfully /// /// - returns: The result of the match. Nil if unsuccessful - private func matchPattern(pattern: Pattern, inRange bounds: NSRange) -> ResultSet? { - if let match = pattern.match { - if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { - if resultSet.range.length != 0 { - return resultSet - } - } - } else if let begin = pattern.begin, _ = pattern.end { - guard let beginResults = matchExpression(begin, inRange: bounds, captures: pattern.beginCaptures) else { - return nil - } - + private func matchAfterBeginOfPattern(pattern: Pattern, beginResults: ResultSet, inRange bounds: NSRange) -> ResultSet? { let newLocation = NSMaxRange(beginResults.range) - guard let endResults = matchPatterns(pattern.subpatterns, withEndPatternFromPattern: pattern, startingAtIndex: newLocation, stopIndex: (string as NSString).length) else { + guard let endResults = matchSubpatternsOfPattern(pattern, inRange: NSRange(location: newLocation, length: (string as NSString).length - newLocation)) else { return nil } @@ -294,8 +281,6 @@ public class Parser { result.addResults(beginResults) result.addResults(endResults) return result - } - return nil } /// Matches a given regular expression in a String and returns range From 5f9aea89fa9aea933459aad88ffbbe19fe0b5b49 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 19:26:04 +0200 Subject: [PATCH 065/110] closes #1 Finish documenting --- SyntaxKit/AttributedParser.swift | 4 ++ SyntaxKit/AttributedParsingOperation.swift | 32 +++++---- SyntaxKit/Parser.swift | 79 +++++++++------------- SyntaxKit/Pattern.swift | 7 ++ SyntaxKit/Tests/TestHelper.swift | 24 +++---- 5 files changed, 73 insertions(+), 73 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 6b1fd53..34c641a 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -2,6 +2,10 @@ // AttributedParser.swift // SyntaxKit // +// A subclass of Parser that knows about themes. Using the theme it maps +// between recognized TextMate scope descriptions and NSAttributedString +// attributes. +// // Created by Sam Soffes on 9/24/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index a416043..9b641a2 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -5,6 +5,16 @@ // Subclass of NSOperation that can be used for mutithreaded incremental // parsing with all the benefits of NSOperationQueue. // +// It's underlying parser is an attributed parser. In theory this could be +// refactored into a superclass that uses parser and a subclass that uses +// attributed parser, but honestly I don't see a use-case of ParsingOperation +// so there is only this class. +// +// Note that the callback returns an array of results instead of each result +// separately. This is more efficient since it allows coalescing the edits +// between a beginEditing and an endEditing call. Parser uses the other way for +// backward compatibility reasons. +// // Created by Alexander Hedges on 17/04/16. // Copyright © 2016 Alexander Hedges. All rights reserved. // @@ -16,6 +26,7 @@ struct Diff { var change: String var range: NSRange + // MARK: - Operations /// - returns: true if predicted change from diff matches the characters @@ -59,7 +70,6 @@ public class AttributedParsingOperation: NSOperation { // MARK: - Properties private let parser: AttributedParser - private var stringToParse: String private var operationCallback: OperationCallback private var scopedStringResult: ScopedString @@ -70,17 +80,16 @@ public class AttributedParsingOperation: NSOperation { // MARK: - Initializers public init(string: String, language: Language, theme: Theme, callback: OperationCallback) { - stringToParse = string parser = AttributedParser(language: language, theme: theme) + parser.string = string operationCallback = callback scopedStringResult = ScopedString(string: string) super.init() } public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback) { - stringToParse = string parser = previousOperation.parser - parser.aborted = false + parser.string = string operationCallback = callback scopedStringResult = previousOperation.scopedStringResult @@ -96,7 +105,7 @@ public class AttributedParsingOperation: NSOperation { if diff.representsChangesfromOldString(previousOperation.scopedStringResult.underlyingString, toNewString: string) { self.diff = diff - self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range, previousScopedString: scopedStringResult) + self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range) } } @@ -105,7 +114,6 @@ public class AttributedParsingOperation: NSOperation { public override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] - parser.string = stringToParse var incrementalParsingInfo: (NSRange, Diff, ScopedString)? if range != nil && diff != nil { @@ -147,13 +155,11 @@ public class AttributedParsingOperation: NSOperation { /// In fact passing anything other than this range to parse might lead to /// uninteded results but is not prohibited. /// This method is only guaranteed to possibly not return nil if parse was - /// called on the old string before this call. The only kinds of changed - /// supported are single insertions and deletions of strings. + /// called on the old string before this call. /// /// - parameter newString: The examined new string. Should be the product /// of previously parsed + change. - /// - parameter insertion: If the change applied to the old value is an - /// insertion as opposed to a deletion. + /// - parameter insertion: Change is an insertion as opposed to a deletion. /// - parameter range: The range in which the change occurred. In case /// of an insertion the range in the new string that /// was inserted. For a deletion it is the range in @@ -161,12 +167,12 @@ public class AttributedParsingOperation: NSOperation { /// /// - returns: A range in newString that can be safely re-parsed. Or nil if /// everything has to be reparsed. - func outdatedRangeForChangeInString(newString: NSString, changeIsInsertion insertion: Bool, changedRange range: NSRange, previousScopedString previousScopes: ScopedString) -> NSRange? { - if !Diff.stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: previousScopes.underlyingString) { + func outdatedRangeForChangeInString(newString: NSString, changeIsInsertion insertion: Bool, changedRange range: NSRange) -> NSRange? { + if !Diff.stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: scopedStringResult.underlyingString) { return nil } - var potentialNewString = previousScopes + var potentialNewString = scopedStringResult let linesRange: NSRange if insertion { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 43ccaf0..79adaa8 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -6,7 +6,8 @@ // patterns. It tries to match parsing behavior of TextMate as closely as // possible. // -// The parsed string is stored as a property. +// The parsed string is stored as a property. This is so the regex library +// recognizes subsequent matches as such. // // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. @@ -23,16 +24,14 @@ public class Parser { public let language: Language + var string = "" var aborted = false - var string: String - // MARK: - Initializers public init(language: Language) { self.language = language - self.string = "" } @@ -50,28 +49,17 @@ public class Parser { // MARK: - Private - // Implementation notes: - // The first part tries to find the context in which parsing should take - // place (which block we are in), if any. - // The second part parses the string the until the full range is consumed - // and it may exceed that range if further parts of the string are outdated - - /// Parses the given string in the given range and calls the callback on - /// every match of a scope + /// Parses the string. Supports incremental parsing. /// - /// If a range is treated more of a recommendation than a requirement. - /// For best results supply a range that was returned from - /// outdatedRangeForChangeInString called before calling this method. + /// The given range mey be exceeded if necessary to match a pattern entirely. /// - /// - parameter string: The string that is parsed - /// - parameter range: The range in which the string should be parsed. - /// On nil the entire string will be parsed. - /// - parameter diff: Addition: "Added string", (insertionIndex, 0) - /// Deletion: "", (deletionStart, deletionLength) - /// - parameter scopes: Denotes - /// - parameter callback: The callback to call on every match of a - /// pattern identifier of the language - /// - returns: A scopedString that contains the range results of the parsing + /// - parameter incremental: A tuple containing all the information + /// necessary for incremental parsing. A range in which to parse, + /// a Diff representing the change and the result of the + /// previous call to parse. + /// - parameter callback: The callback to call on every match of a pattern + /// identifier of the language. + /// - returns: A ScopedString that contains the range results of the parsing /// Or nil if the parsing was aborted. func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: Callback) -> ScopedString? { let bounds: NSRange @@ -206,21 +194,16 @@ public class Parser { } /// Helper method that iterates over the given patterns and tries to match - /// them in order. - /// - /// It returns the best match, if many are possible. Which is the result - /// that starts the soonest and is encountered first. + /// them. Returns the matched pattern with the highest priority + /// (first criterion: matched sooner, second: higher up the list). /// /// - parameter patterns: The patterns that should be matched - /// - parameter string: The string that should be matched against /// - parameter range: The range in which the matching should happen. - /// Though it is not guaranteed that the length of - /// the result does not exceed the length of the - /// range. /// /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any - /// information associated with the match. + /// information associated with the match. The results range may + /// exceed the passed in range. private func findMatchFromPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { var interestingBounds = bounds var bestResult: (pattern: Pattern, match: ResultSet)? @@ -236,6 +219,9 @@ public class Parser { return bestResult } + /// Matches a single pattern in the string in the given range + /// + /// - returns: The result of the match. Nil if unsuccessful private func firstMatchOfPattern(pattern: Pattern, inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { if let match = pattern.match { if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { @@ -257,28 +243,26 @@ public class Parser { // The order in which the beginning middle and end are added to the final // result matters. - /// Matches a single pattern in the string in the given range + /// Matches the middle and end of the given pattern /// - /// A pattern may be one three options: - /// * A single pattern called match which should be matched - /// * A begin and an end pattern containing an optional body of patterns - /// which should be matched between the begin and the end - /// * Only a body of patterns without the begin and end. Any pattern may be - /// matched successfully - /// - /// - returns: The result of the match. Nil if unsuccessful - private func matchAfterBeginOfPattern(pattern: Pattern, beginResults: ResultSet, inRange bounds: NSRange) -> ResultSet? { - let newLocation = NSMaxRange(beginResults.range) + /// - parameter pattern: The pattern whose subpatterns and end pattern + /// has to be matched + /// - parameter begin: The match result of the beginning + /// - parameter range: The range in which to perform the match + /// - returns: The result of matching the given pattern or nil on abortion. + /// It's range may exceed the passed in range. + private func matchAfterBeginOfPattern(pattern: Pattern, beginResults begin: ResultSet, inRange bounds: NSRange) -> ResultSet? { + let newLocation = NSMaxRange(begin.range) guard let endResults = matchSubpatternsOfPattern(pattern, inRange: NSRange(location: newLocation, length: (string as NSString).length - newLocation)) else { return nil } let result = ResultSet(startingRange: endResults.range) if pattern.name != nil { - result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(beginResults.range, endResults.range))) + result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(begin.range, endResults.range))) } - result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: beginResults.range.location + beginResults.range.length, length: NSUnionRange(beginResults.range, endResults.range).length - beginResults.range.length), attribute: pattern)) - result.addResults(beginResults) + result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: begin.range.location + begin.range.length, length: NSUnionRange(begin.range, endResults.range).length - begin.range.length), attribute: pattern)) + result.addResults(begin) result.addResults(endResults) return result } @@ -287,7 +271,6 @@ public class Parser { /// information for the captures /// /// - parameter expression: The regular expression to match - /// - parameter string: The string to match against /// - parameter range: The range to which to restrict the match /// - parameter captures: A collection of captures that can be used to /// add extra information to parts of the match. diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 2fb70a7..2a2acea 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -8,6 +8,13 @@ // in the same or another grammar. It is only usable as a pattern after it has // been resolved via the provided method (and has type .resolved). // +// A pattern may be one of three types: +// * A single pattern in match which should be matched +// * A begin and an end pattern containing an optional body of patterns +// (subpatterns) which should be matched between the begin and the end +// * Only a body of patterns without the begin and end. Any pattern may be +// matched successfully +// // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index b322430..0af7aa7 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -15,19 +15,19 @@ func fixture(name: String, _ type: String) -> String! { return try! String(contentsOfFile: path) } -func language(name: String) -> Language! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! - let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - var language = Language(dictionary: plist)! - language.validateWithHelperLanguages([]) - return language -} +//func language(name: String) -> Language! { +// let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! +// let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] +// var language = Language(dictionary: plist)! +// language.validateWithHelperLanguages([]) +// return language +//} -func theme(name: String) -> Theme! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmTheme")! - let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] - return Theme(dictionary: plist)! -} +//func theme(name: String) -> Theme! { +// let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmTheme")! +// let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] +// return Theme(dictionary: plist)! +//} func simpleTheme() -> Theme! { return Theme(dictionary: [ From efae43422babbc40bbf1ea6a40c9cb2697aa15a6 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 19:36:54 +0200 Subject: [PATCH 066/110] add note about AttributedParsingOperation to Readme --- Readme.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Readme.markdown b/Readme.markdown index b0ce578..201478d 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -83,6 +83,9 @@ let attributedString = attributedParser.attributedStringForString(input) Easy as that. This method takes an optional `baseAttributes` parameter to customize how the string is created. This is great if you want to specify a font, etc. +### Parsing Operations + +There is also a `AttributedParsingOperation` subclass of NSOperation that facilitates mutithreaded parsing. ### Custom Parsers From f0730a6a4145e6fe05d17ef1dd060e4373353ac3 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 21:44:39 +0200 Subject: [PATCH 067/110] fix some tests --- SyntaxKit/Parser.swift | 7 +++---- SyntaxKit/ScopedString.swift | 6 ++++-- SyntaxKit/Tests/AttributedParserTests.swift | 9 ++++++++- SyntaxKit/Tests/IncrementalParsingTests.swift | 8 ++------ SyntaxKit/Tests/LanguageTests.swift | 8 +++++++- SyntaxKit/Tests/ParserTests.swift | 16 ++++++++++++---- .../Tests/Resources/Fixtures/Swift.tmLanguage | 8 ++++---- SyntaxKit/Tests/ScopedStringTests.swift | 18 ++++++++---------- .../SwiftBaselineHighlightingTests.swift | 11 ++++++++++- SyntaxKit/Tests/TestHelper.swift | 19 ++++++------------- SyntaxKit/Tests/ThemeTests.swift | 11 +++++++++-- 11 files changed, 73 insertions(+), 48 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 79adaa8..c7998ca 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -6,9 +6,6 @@ // patterns. It tries to match parsing behavior of TextMate as closely as // possible. // -// The parsed string is stored as a property. This is so the regex library -// recognizes subsequent matches as such. -// // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // @@ -174,9 +171,11 @@ public class Parser { } else { resultForMiddle = matchAfterBeginOfPattern(bestMatchForMiddle!.pattern, beginResults: bestMatchForMiddle!.match, inRange: range) } + if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } + result.addResults(resultForMiddle!) let newStart = NSMaxRange(resultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) @@ -320,7 +319,7 @@ public class Parser { callback(scope: Language.globalScope, range: results.range) for result in results.results where result.range.length > 0 { if result.attribute != nil { - scopesString.addScopeAtBottom(result as Scope) + scopesString.addScopeAtTop(result as Scope) } else if result.patternIdentifier != "" { callback(scope: result.patternIdentifier, range: result.range) } diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index aeadc9f..490e174 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -109,7 +109,8 @@ struct ScopedString { return index >= 0 && index <= baseScope.range.length } - mutating func addScopeAtBottom(scope: Scope) { + mutating func addScopeAtTop(scope: Scope) { + assert(scope.range.length != 0) assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false @@ -125,7 +126,8 @@ struct ScopedString { } } - mutating func addScopeAtTop(scope: Scope) { + mutating func addScopeAtBottom(scope: Scope) { + assert(scope.range.length != 0) assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 86432d9..2986f46 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -13,11 +13,18 @@ class AttributedParserTests: XCTestCase { // MARK: - Properties - let parser = AttributedParser(language: language("YAML"), theme: simpleTheme()) + let manager = getBundleManager() + var parser: AttributedParser! // MARK: - Tests + override func setUp() { + super.setUp() + let yaml = manager.languageWithIdentifier("source.YAML")! + parser = AttributedParser(language: yaml, theme: simpleTheme()) + } + func testParsing() { let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index ac1daa3..316a647 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -11,16 +11,12 @@ import SyntaxKit class IncrementalParsingTests: XCTestCase { - let parser = Parser(language: language("Swift")) +// let parser = Parser(language: language("Swift")) override func setUp() { super.setUp() } - - override func tearDown() { - super.tearDown() - } - + func testEdits() { // let input = fixture("swifttest.swift", "txt") // diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index ebfe9c9..3896161 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -13,11 +13,17 @@ class LanguageTests: XCTestCase { // MARK: - Properties - let yaml = language("YAML") + let manager = getBundleManager() + var yaml: Language! // MARK: - Tests + override func setUp() { + super.setUp() + yaml = manager.languageWithIdentifier("source.YAML") + } + func testLoading() { XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) XCTAssertEqual("YAML", yaml.name) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 538c85f..f982648 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -13,11 +13,18 @@ class ParserTests: XCTestCase { // MARK: - Properties - let parser = Parser(language: language("YAML")) - + var parser: Parser! + let manager = getBundleManager() // MARK: - Tests + override func setUp() { + super.setUp() + let yaml = manager.languageWithIdentifier("source.YAML")! + parser = Parser(language: yaml) + } + + func testParsingBeginEnd() { var stringQuoted: NSRange? var punctuationBegin: NSRange? @@ -42,7 +49,7 @@ class ParserTests: XCTestCase { XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) } - func testParsingBeginEndCrap() { + func testParsingBeginEndGarbage() { var stringQuoted: NSRange? parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (result: (scope: String, range: NSRange)) in @@ -60,7 +67,8 @@ class ParserTests: XCTestCase { } func testRuby() { - let parser = Parser(language: language("Ruby")) + let ruby = manager.languageWithIdentifier("source.Ruby")! + parser = Parser(language: ruby) let input = fixture("test.rb", "txt") parser.parse(input, match: { _ in return }) } diff --git a/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage index bb91fa2..2ce68ba 100644 --- a/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage +++ b/SyntaxKit/Tests/Resources/Fixtures/Swift.tmLanguage @@ -552,7 +552,7 @@ end - (?!\G) + \n patterns @@ -567,7 +567,7 @@ end - \n + (?=\n) name comment.line.double-slash.swift @@ -586,7 +586,7 @@ end - (?!\G) + \n patterns @@ -601,7 +601,7 @@ end - \n + (?=\n) name comment.line.triple-slash.swift diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index b01588f..583ab65 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -28,6 +28,8 @@ class ScopedStringTests: XCTestCase { let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) newScopedString.addScopeAtTop(newScope1) +// print(newScopedString.prettyRepresentation()) + XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) @@ -37,15 +39,17 @@ class ScopedStringTests: XCTestCase { let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) newScopedString.addScopeAtTop(newScope2) +// print(newScopedString.prettyRepresentation()) + XCTAssert(newScopedString.numberOfScopes() == 3) XCTAssert(newScopedString.numberOfLevels() == 3) XCTAssert(newScopedString.topmostScopeAtIndex(1) == newScope1) XCTAssert(newScopedString.topmostScopeAtIndex(2) == newScope2) - XCTAssertFalse(newScopedString.numberOfScopes() == 1) newScopedString.deleteCharactersInRange(NSRange(location: 2, length: 1)) +// print(newScopedString.prettyRepresentation()) XCTAssert(newScopedString.underlyingString == "Tet") XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.numberOfLevels() == 2) @@ -53,18 +57,12 @@ class ScopedStringTests: XCTestCase { XCTAssert(newScopedString.topmostScopeAtIndex(1).range == NSRange(location: 1, length: 2)) newScopedString.insertString("ssssss", atIndex: 2) +// print(newScopedString.prettyRepresentation()) XCTAssert(newScopedString.underlyingString == "Tesssssst") XCTAssert(newScopedString.numberOfScopes() == 2) - - let newScope3 = Scope(identifier: "zeroLengthScope1", range: NSRange(location: 0, length: 0), attribute: nil) - newScopedString.addScopeAtTop(newScope3) - let newScope4 = Scope(identifier: "zeroLengthScope2", range: NSRange(location: 3, length: 0), attribute: nil) - newScopedString.addScopeAtTop(newScope4) - XCTAssert(newScopedString.numberOfScopes() == 4) - XCTAssert(newScopedString.topmostScopeAtIndex(0).patternIdentifier == "zeroLengthScope1") - + newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) - XCTAssert(newScopedString.numberOfScopes() == 3) + XCTAssert(newScopedString.numberOfScopes() == 2) XCTAssert(newScopedString.topmostScopeAtIndex(2).range == NSRange(location: 1, length: 8)) } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 1c53b8d..f32667c 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -12,10 +12,19 @@ import SyntaxKit class SwiftBaselineHighlightingTests: XCTestCase { - let parser = AttributedParser(language: language("Swift"), theme: theme("Solarized")) + // MARK: - Properties + + let manager = getBundleManager() + var parser: AttributedParser! + + + // MARK: - Tests override func setUp() { super.setUp() + let swift = manager.languageWithIdentifier("source.Swift")! + let solarized = manager.themeWithIdentifier("Solarized")! + parser = AttributedParser(language: swift, theme: solarized) } override func tearDown() { diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 0af7aa7..0902e1c 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -15,19 +15,12 @@ func fixture(name: String, _ type: String) -> String! { return try! String(contentsOfFile: path) } -//func language(name: String) -> Language! { -// let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmLanguage")! -// let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] -// var language = Language(dictionary: plist)! -// language.validateWithHelperLanguages([]) -// return language -//} - -//func theme(name: String) -> Theme! { -// let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: "tmTheme")! -// let plist = NSDictionary(contentsOfFile: path)! as [NSObject: AnyObject] -// return Theme(dictionary: plist)! -//} +func getBundleManager() -> BundleManager { + return BundleManager() { identifier, isLanguage in + let name = isLanguage ? identifier.componentsSeparatedByString(".")[1] + ".tmLanguage" : identifier + ".tmTheme" + return NSBundle(forClass: LanguageTests.self).bundleURL.URLByAppendingPathComponent(name) + } +} func simpleTheme() -> Theme! { return Theme(dictionary: [ diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 9dfc14e..cb93cc9 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -13,12 +13,19 @@ class ThemeTests: XCTestCase { // MARK: - Properties - let tomorrow = theme("Tomorrow") - let solarized = theme("Solarized") + let manager = getBundleManager() + var tomorrow: Theme! + var solarized: Theme! // MARK: - Tests + override func setUp() { + super.setUp() + tomorrow = manager.themeWithIdentifier("Tomorrow") + solarized = manager.themeWithIdentifier("Solarized") + } + func testLoading() { XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) XCTAssertEqual("Tomorrow", tomorrow.name) From 06bd0bafdd220204b8d0508d0c269b1b36d2c905 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 16 Jul 2016 22:46:40 +0200 Subject: [PATCH 068/110] fix premature release and some more tests --- SyntaxKit/Pattern.swift | 2 +- SyntaxKit/RefernceManager.swift | 3 ++ SyntaxKit/Tests/LanguageTests.swift | 20 +++++++----- SyntaxKit/Tests/ScopedStringTests.swift | 42 ++++++++++++------------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 2a2acea..7fbabb1 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -150,7 +150,7 @@ class Include: Pattern { private var _type: referenceType private let repositoryRef: String? private let languageRef: String? - private weak var associatedRepository: Repository? + private var associatedRepository: Repository? // MARK: - Initializers diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 86aac69..8d6104a 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -35,6 +35,9 @@ class ReferenceManager { var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { + if include == "#inline" { + print("Here") + } let reference = Include(reference: include, inRepository: repository, parent: caller, bundleManager: bundleManager!) self.includes.append(reference) results.append(reference) diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 3896161..a849308 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -14,17 +14,12 @@ class LanguageTests: XCTestCase { // MARK: - Properties let manager = getBundleManager() - var yaml: Language! // MARK: - Tests - override func setUp() { - super.setUp() - yaml = manager.languageWithIdentifier("source.YAML") - } - - func testLoading() { + func testYaml() { + let yaml = manager.languageWithIdentifier("source.YAML")! XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) @@ -42,4 +37,15 @@ class LanguageTests: XCTestCase { XCTAssertEqual("string.unquoted.yaml", pattern.name) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) } + + func testSwift() { + let swift = manager.languageWithIdentifier("source.swift")! + XCTAssertEqual("D133338A-DEED-4ECC-9852-A392C44D10AC", swift.UUID) + XCTAssertEqual("Swift", swift.name) + XCTAssertEqual("source.swift", swift.scopeName) + + XCTAssertEqual("comment.line.shebang.swift", swift.pattern.subpatterns[0].name) + XCTAssertEqual(4, swift.pattern.subpatterns[1].subpatterns.count) + XCTAssertEqual("comment.line.double-slash.swift", swift.pattern.subpatterns[1].subpatterns[3].subpatterns[0].name) + } } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 583ab65..6f119d8 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -21,50 +21,50 @@ class ScopedStringTests: XCTestCase { func testScopesString() { var newScopedString = ScopedString(string: "Test") - XCTAssert(newScopedString.numberOfScopes() == 1) - XCTAssert(newScopedString.numberOfLevels() == 1) + XCTAssertEqual(newScopedString.numberOfScopes(), 1) + XCTAssertEqual(newScopedString.numberOfLevels(), 1) - XCTAssert(newScopedString.topmostScopeAtIndex(2) == newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScopedString.baseScope) let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) newScopedString.addScopeAtTop(newScope1) // print(newScopedString.prettyRepresentation()) - XCTAssert(newScopedString.numberOfScopes() == 2) - XCTAssert(newScopedString.numberOfLevels() == 2) + XCTAssertEqual(newScopedString.numberOfScopes(), 2) + XCTAssertEqual(newScopedString.numberOfLevels(), 2) - XCTAssert(newScopedString.topmostScopeAtIndex(0) == newScopedString.baseScope) - XCTAssert(newScopedString.topmostScopeAtIndex(1) == newScope1) - XCTAssert(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1) == newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(0), newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) + XCTAssertEqual(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1), newScopedString.baseScope) let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) newScopedString.addScopeAtTop(newScope2) // print(newScopedString.prettyRepresentation()) - XCTAssert(newScopedString.numberOfScopes() == 3) - XCTAssert(newScopedString.numberOfLevels() == 3) + XCTAssertEqual(newScopedString.numberOfScopes(), 3) + XCTAssertEqual(newScopedString.numberOfLevels(), 3) - XCTAssert(newScopedString.topmostScopeAtIndex(1) == newScope1) - XCTAssert(newScopedString.topmostScopeAtIndex(2) == newScope2) - XCTAssertFalse(newScopedString.numberOfScopes() == 1) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScope2) + XCTAssertNotEqual(newScopedString.numberOfScopes(), 1) newScopedString.deleteCharactersInRange(NSRange(location: 2, length: 1)) // print(newScopedString.prettyRepresentation()) - XCTAssert(newScopedString.underlyingString == "Tet") - XCTAssert(newScopedString.numberOfScopes() == 2) - XCTAssert(newScopedString.numberOfLevels() == 2) + XCTAssertEqual(newScopedString.underlyingString, "Tet") + XCTAssertEqual(newScopedString.numberOfScopes(), 2) + XCTAssertEqual(newScopedString.numberOfLevels(), 2) - XCTAssert(newScopedString.topmostScopeAtIndex(1).range == NSRange(location: 1, length: 2)) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(1).range, NSRange(location: 1, length: 2)) newScopedString.insertString("ssssss", atIndex: 2) // print(newScopedString.prettyRepresentation()) - XCTAssert(newScopedString.underlyingString == "Tesssssst") - XCTAssert(newScopedString.numberOfScopes() == 2) + XCTAssertEqual(newScopedString.underlyingString, "Tesssssst") + XCTAssertEqual(newScopedString.numberOfScopes(), 2) newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) - XCTAssert(newScopedString.numberOfScopes() == 2) + XCTAssertEqual(newScopedString.numberOfScopes(), 2) - XCTAssert(newScopedString.topmostScopeAtIndex(2).range == NSRange(location: 1, length: 8)) + XCTAssertEqual(newScopedString.topmostScopeAtIndex(2).range, NSRange(location: 1, length: 8)) } func testRangeExtension() { From fe7193c9abde85f5cdf3b0614bc8f44b6fc44458 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 11:57:12 +0200 Subject: [PATCH 069/110] closes #7 Reenable all tests and slight adjustment to Operation interface --- SyntaxKit/AttributedParsingOperation.swift | 4 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 172 +++++++++--------- 2 files changed, 89 insertions(+), 87 deletions(-) diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index 9b641a2..c4cb647 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -87,10 +87,10 @@ public class AttributedParsingOperation: NSOperation { super.init() } - public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback) { + public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback? = nil) { parser = previousOperation.parser parser.string = string - operationCallback = callback + operationCallback = callback ?? previousOperation.operationCallback scopedStringResult = previousOperation.scopedStringResult super.init() diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 316a647..ac47c51 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -11,104 +11,106 @@ import SyntaxKit class IncrementalParsingTests: XCTestCase { -// let parser = Parser(language: language("Swift")) + // MARK: - Properties + + let manager = getBundleManager() + var parsingOperation: AttributedParsingOperation! + var theme: Theme! + var language: Language! + var totalRange: NSRange? + var input = "" + + + // MARK: - Tests override func setUp() { super.setUp() + language = manager.languageWithIdentifier("Source.swift")! + theme = manager.themeWithIdentifier("tomorrow")! + } - + func testEdits() { -// let input = fixture("swifttest.swift", "txt") -// -// var rangeToParse = parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 0, length: 5)) -// XCTAssertEqual(rangeToParse, nil) -// -// parser.parse(input, inRange: rangeToParse) { _ in return } -// -// var newInput = stringByReplacingRange(NSRange(location: 20, length: 0), inString: input, withString: " ") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 20, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 3, length: 136)) -// -// newInput = stringByReplacingRange(NSRange(location: 162, length: 0), inString: input, withString: "i") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 162, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 5)) -// -// parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// newInput = stringByReplacingRange(NSRange(location: 162, length: 1), inString: newInput, withString: "") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 162, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 4)) -// -// parser.parse(input, inRange: rangeToParse) { _ in return } -// -// newInput = stringByReplacingRange(NSRange(location: 160, length: 0), inString: input, withString: "756") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 160, length: 3)) -// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) -// -// newInput = stringByReplacingRange(NSRange(location: 159, length: 0), inString: input, withString: "756") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 159, length: 3)) -// XCTAssertEqual(rangeToParse, NSRange(location: 159, length: 7)) + input = fixture("swifttest.swift", "txt") + parsingOperation = getParsingOperation() + + parsingOperation.main() + XCTAssertEqual(totalRange!, NSRange(location: 0, length: (input as NSString).length)) + + testInsertion("i", location: 162, expectedRange: NSRange(location: 159, length: 5)) + + testDeletion(NSRange(location: 162, length: 1), expectedRange: NSRange(location: 159, length: 4)) + + testInsertion("756", location: 160, expectedRange: NSRange(location: 159, length: 7)) } func testEdgeCase() { -// let input = "// test.swift\n/**" -// -// parser.parse(input) { _ in return } -// -// var newInput = stringByReplacingRange(NSRange(location: 2, length: 1), inString: input, withString: "") -// var rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 2, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 13)) -// -// parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// newInput = stringByReplacingRange(NSRange(location: 2, length: 0), inString: newInput, withString: " ") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 2, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 0, length: 14)) -// -// parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// newInput = stringByReplacingRange(NSRange(location: 17, length: 0), inString: newInput, withString: "\n") -// rangeToParse = parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 17, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 14, length: 4)) -// -// parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// parser.parse("") { _ in return } + input = "// test.swift\n/**" + parsingOperation = getParsingOperation() + + parsingOperation.main() + XCTAssertEqual(totalRange, NSRange(location: 0, length: 17)) + + testDeletion(NSRange(location: 2, length: 1), expectedRange: NSRange(location: 0, length: 13)) + + testInsertion(" ", location: 2, expectedRange: NSRange(location: 0, length: 14)) + + testInsertion("\n", location: 17, expectedRange: NSRange(location: 14, length: 4)) } func testPerformanceInScope() { -// let input = fixture("swifttest.swift", "txt") -// self.parser.parse(input) { _ in return } -// -// self.measureBlock { -// let newInput = stringByReplacingRange(NSRange(location: 239, length: 0), inString: input, withString: "Tests") -// var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: true, changedRange: NSRange(location: 239, length: 5)) -// XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 24)) -// -// self.parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: false, changedRange: NSRange(location: 239, length: 5)) -// XCTAssertEqual(rangeToParse, NSRange(location: 230, length: 19)) -// -// self.parser.parse(input, inRange: rangeToParse) { _ in return } -// } + input = fixture("swifttest.swift", "txt") + parsingOperation = getParsingOperation() + + parsingOperation.main() + + self.measureBlock { + self.testInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) + + self.testDeletion(NSRange(location: 239, length: 5), expectedRange: NSRange(location: 230, length: 19)) + } } func testPerformanceEdgeCases() { -// let input = fixture("swifttest.swift", "txt") -// self.parser.parse(input) { _ in return } -// -// self.measureBlock { -// let newInput = stringByReplacingRange(NSRange(location: 139, length: 1), inString: input, withString: "") -// var rangeToParse = self.parser.outdatedRangeForChangeInString(newInput, changeIsInsertion: false, changedRange: NSRange(location: 139, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 22)) -// -// self.parser.parse(newInput, inRange: rangeToParse) { _ in return } -// -// rangeToParse = self.parser.outdatedRangeForChangeInString(input, changeIsInsertion: true, changedRange: NSRange(location: 139, length: 1)) -// XCTAssertEqual(rangeToParse, NSRange(location: 139, length: 4)) -// -// self.parser.parse(input, inRange: rangeToParse) { _ in return } -// } + input = fixture("swifttest.swift", "txt") + parsingOperation = getParsingOperation() + + parsingOperation.main() + + self.measureBlock { + self.testDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) + + self.testInsertion("/", location: 139, expectedRange: NSRange(location: 139, length: 23)) + } + } + + private func getParsingOperation() -> AttributedParsingOperation { + return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)]) in + for result in results { + if self.totalRange == nil { + self.totalRange = result.range + } else { + self.totalRange = NSUnionRange(self.totalRange!, result.range) + } + } + } + } + + private func testInsertion(string: String, location: Int, expectedRange expected: NSRange) { + input = stringByReplacingRange(NSRange(location: location, length: 0), inString: input, withString: string) + parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) + + totalRange = nil + parsingOperation.main() + XCTAssertEqual(totalRange!, expected) + } + + private func testDeletion(range: NSRange, expectedRange expected: NSRange) { + input = stringByReplacingRange(range, inString: input, withString: "") + parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: false, changedRange: range) + + totalRange = nil + parsingOperation.main() + XCTAssertEqual(totalRange, expected) } } From 48e95cf6104313a1344b8c8f7e8734c87e24bfc9 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 14:12:22 +0200 Subject: [PATCH 070/110] add useful changes from \G parsing branch Speed up parser by only asking for the first occurence of a match. --- .../36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 2 +- SyntaxKit/Parser.swift | 16 ++++++---------- SyntaxKit/RefernceManager.swift | 3 --- SyntaxKit/Tests/IncrementalParsingTests.swift | 3 +++ .../Tests/SwiftBaselineHighlightingTests.swift | 4 ---- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist index 2b2c420..122bd80 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -64,7 +64,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.085 + 0.065 baselineIntegrationDisplayName Local Baseline diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index c7998ca..21d822e 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -35,9 +35,8 @@ public class Parser { // MARK: - Public public func parse(string: String, match callback: Callback) { - if aborted { - return - } + if aborted { return } + self.string = string parse(match: callback) self.string = "" @@ -109,9 +108,8 @@ public class Parser { } } - if aborted { - return nil - } + if aborted { return nil } + scopesString.removeScopesInRange(allResults.range) self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) return scopesString @@ -149,9 +147,7 @@ public class Parser { var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { - if aborted { - return nil - } + if aborted { return nil } let bestMatchForMiddle = findMatchFromPatterns(pattern.subpatterns, inRange: range) @@ -280,7 +276,7 @@ public class Parser { /// could not match any part of the string. It may also be empty /// and only contain range information to show what it matched. private func matchExpression(regularExpression: NSRegularExpression, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { - guard let result = regularExpression.matchesInString(string, options: [.WithTransparentBounds], range: bounds).first else { + guard let result = regularExpression.firstMatchInString(string, options: [.WithTransparentBounds], range: bounds) else { return nil } diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 8d6104a..86aac69 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -35,9 +35,6 @@ class ReferenceManager { var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { - if include == "#inline" { - print("Here") - } let reference = Include(reference: include, inRepository: repository, parent: caller, bundleManager: bundleManager!) self.includes.append(reference) results.append(reference) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index ac47c51..c842d71 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -84,6 +84,9 @@ class IncrementalParsingTests: XCTestCase { } } + + // MARK: - Helpers + private func getParsingOperation() -> AttributedParsingOperation { return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)]) in for result in results { diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index f32667c..7362c43 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -27,10 +27,6 @@ class SwiftBaselineHighlightingTests: XCTestCase { parser = AttributedParser(language: swift, theme: solarized) } - override func tearDown() { - super.tearDown() - } - func testColors() { let input = fixture("swifttest.swift", "txt") let string = parser.attributedStringForString(input) From 4ebcb823ca1c49dc4bc057d37d2a73060dd6591b Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 14:15:29 +0200 Subject: [PATCH 071/110] update baselines for tests --- .../36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist index 122bd80..92d73e9 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist +++ b/SyntaxKit.xcodeproj/xcshareddata/xcbaselines/211989AF1B2EC3B600F0D786.xcbaseline/36C0E679-EC89-495E-854F-FFBDEA1A4F58.plist @@ -21,7 +21,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.003 + 0.002 baselineIntegrationDisplayName Local Baseline @@ -41,7 +41,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.0037323 + 0.002 baselineIntegrationDisplayName Local Baseline From 087ec746a5c581a0e13f1ea32c03e5db4a9a6074 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 15:13:24 +0200 Subject: [PATCH 072/110] make it compile on all platforms without warnings --- SyntaxKit.xcodeproj/project.pbxproj | 42 +++++++------------ SyntaxKit/BundleManager.swift | 6 +-- SyntaxKit/Pattern.swift | 1 + .../{YAML.tmLanguage => Yaml.tmLanguage} | 0 SyntaxKit/Tests/TestHelper.swift | 5 ++- 5 files changed, 21 insertions(+), 33 deletions(-) rename SyntaxKit/Tests/Resources/Fixtures/{YAML.tmLanguage => Yaml.tmLanguage} (100%) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index fdaedcc..3f91f0f 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -14,7 +14,7 @@ 210BF2701B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 210BF26F1B37C0A2008AA4F0 /* Ruby.tmLanguage */; }; 210BF2711B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 210BF26F1B37C0A2008AA4F0 /* Ruby.tmLanguage */; }; 211989801B2EAF0900F0D786 /* Tomorrow.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */; }; - 211989811B2EAF0900F0D786 /* YAML.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 2119897F1B2EAF0900F0D786 /* YAML.tmLanguage */; }; + 211989811B2EAF0900F0D786 /* Yaml.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 2119897F1B2EAF0900F0D786 /* Yaml.tmLanguage */; }; 211989841B2EB18600F0D786 /* TestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989821B2EB18000F0D786 /* TestHelper.swift */; }; 211989891B2EB8D400F0D786 /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989881B2EB8D400F0D786 /* ParserTests.swift */; }; 2119898B1B2EBA2C00F0D786 /* ThemeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2119898A1B2EBA2C00F0D786 /* ThemeTests.swift */; }; @@ -38,7 +38,7 @@ 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989941B2EC38B00F0D786 /* ResultSet.swift */; }; 211989C81B2EC40500F0D786 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989961B2EC38B00F0D786 /* Theme.swift */; }; 211989C91B2EC40900F0D786 /* Tomorrow.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */; }; - 211989CA1B2EC40900F0D786 /* YAML.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 2119897F1B2EAF0900F0D786 /* YAML.tmLanguage */; }; + 211989CA1B2EC40900F0D786 /* Yaml.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 2119897F1B2EAF0900F0D786 /* Yaml.tmLanguage */; }; 211989CB1B2EC40C00F0D786 /* TestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989821B2EB18000F0D786 /* TestHelper.swift */; }; 211989CC1B2EC40C00F0D786 /* ParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211989881B2EB8D400F0D786 /* ParserTests.swift */; }; 211989CD1B2EC40C00F0D786 /* AttributedParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210299D01B2E8924009C61EE /* AttributedParserTests.swift */; }; @@ -65,7 +65,7 @@ 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; - 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; + 8CAEC6BA1D3BB297001C57D3 /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; @@ -137,7 +137,7 @@ 210BF26C1B37C04E008AA4F0 /* test.rb.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.rb.txt; sourceTree = ""; }; 210BF26F1B37C0A2008AA4F0 /* Ruby.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Ruby.tmLanguage; sourceTree = ""; }; 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Tomorrow.tmTheme; sourceTree = ""; }; - 2119897F1B2EAF0900F0D786 /* YAML.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = YAML.tmLanguage; sourceTree = ""; }; + 2119897F1B2EAF0900F0D786 /* Yaml.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Yaml.tmLanguage; sourceTree = ""; }; 211989821B2EB18000F0D786 /* TestHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestHelper.swift; sourceTree = ""; }; 211989881B2EB8D400F0D786 /* ParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParserTests.swift; sourceTree = ""; }; 2119898A1B2EBA2C00F0D786 /* ThemeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeTests.swift; sourceTree = ""; }; @@ -254,7 +254,7 @@ 210BF26C1B37C04E008AA4F0 /* test.rb.txt */, 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */, 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */, - 2119897F1B2EAF0900F0D786 /* YAML.tmLanguage */, + 2119897F1B2EAF0900F0D786 /* Yaml.tmLanguage */, ); path = Fixtures; sourceTree = ""; @@ -487,7 +487,7 @@ files = ( 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 210BF2711B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */, - 211989CA1B2EC40900F0D786 /* YAML.tmLanguage in Resources */, + 211989CA1B2EC40900F0D786 /* Yaml.tmLanguage in Resources */, 211989C91B2EC40900F0D786 /* Tomorrow.tmTheme in Resources */, 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, 210BF26E1B37C04E008AA4F0 /* test.rb.txt in Resources */, @@ -506,12 +506,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 210BF2701B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */, + 211989811B2EAF0900F0D786 /* Yaml.tmLanguage in Resources */, 211989801B2EAF0900F0D786 /* Tomorrow.tmTheme in Resources */, - 210BF26D1B37C04E008AA4F0 /* test.rb.txt in Resources */, - 211989811B2EAF0900F0D786 /* YAML.tmLanguage in Resources */, - 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, + 210BF26D1B37C04E008AA4F0 /* test.rb.txt in Resources */, + 8CAEC6BA1D3BB297001C57D3 /* swifttest.swift.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -593,7 +594,6 @@ 211989891B2EB8D400F0D786 /* ParserTests.swift in Sources */, 210299DF1B2E892E009C61EE /* AttributedParserTests.swift in Sources */, 8CE6BE2E1C5D1BBA002676BD /* ScopedStringTests.swift in Sources */, - 8CB2FD241C4D878C008ECD6D /* swifttest.swift.txt in Sources */, 210299DE1B2E892E009C61EE /* LanguageTests.swift in Sources */, 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, 2119898B1B2EBA2C00F0D786 /* ThemeTests.swift in Sources */, @@ -814,10 +814,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -839,10 +836,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -861,7 +855,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", ); GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -882,7 +875,6 @@ FRAMEWORK_SEARCH_PATHS = ( "$(DEVELOPER_FRAMEWORKS_DIR)", "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/Mac", ); INFOPLIST_FILE = SyntaxKit/Tests/Resources/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; @@ -899,10 +891,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/watchOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -923,10 +912,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/watchOS", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/SyntaxKit/Resources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index ee1d505..2a4f290 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -70,10 +70,10 @@ public class BundleManager { } self.dependencies = [] - var language = self.getRawLanguageWithIdentifier(identifier)! - language.validateWithHelperLanguages(self.dependencies) + var language = self.getRawLanguageWithIdentifier(identifier) + language?.validateWithHelperLanguages(self.dependencies) - if languageCaching { + if languageCaching && language != nil { self.cachedLanguages[identifier] = language } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 7fbabb1..9d8c314 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -19,6 +19,7 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // +@objc(SKPattern) class Pattern: NSObject { // MARK: - Properties diff --git a/SyntaxKit/Tests/Resources/Fixtures/YAML.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Yaml.tmLanguage similarity index 100% rename from SyntaxKit/Tests/Resources/Fixtures/YAML.tmLanguage rename to SyntaxKit/Tests/Resources/Fixtures/Yaml.tmLanguage diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 0902e1c..44ae2dc 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -17,8 +17,9 @@ func fixture(name: String, _ type: String) -> String! { func getBundleManager() -> BundleManager { return BundleManager() { identifier, isLanguage in - let name = isLanguage ? identifier.componentsSeparatedByString(".")[1] + ".tmLanguage" : identifier + ".tmTheme" - return NSBundle(forClass: LanguageTests.self).bundleURL.URLByAppendingPathComponent(name) + let name = isLanguage ? identifier.componentsSeparatedByString(".")[1] : identifier + let ext = isLanguage ? ".tmLanguage" : ".tmTheme" + return NSBundle(forClass: LanguageTests.self).URLForResource(name.capitalizedString, withExtension: ext) ?? NSURL() } } From 0045e9fa2dc51ea892f9bb7bd08973dc8d621c28 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 15:29:52 +0200 Subject: [PATCH 073/110] remove caveats from readme --- Readme.markdown | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 201478d..c36486a 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -92,17 +92,3 @@ There is also a `AttributedParsingOperation` subclass of NSOperation that facili If you want to build your own parser (for example, to generate HTML) you can subclass whichever one meets your needs. Go nuts. Enjoy. - -## Caveats - -There are however a few things you got to watch out for: - -* \G will always be matched -* Technical differences from onigurama to NSRegularExpression -* Backreferences to begin are not supported -* contentName property is not supported -* Attributes other than foreground color are ignored -* crashs after ~ 300 stackframes in a secondary thread (in debug configuration) -* Cannot recursively include itself (use $self or $base instead) - -Feel free to improve upon this. From 53b85f43a9aa83dbb6d6181d807a5c1c640950db Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Jul 2016 16:39:38 +0200 Subject: [PATCH 074/110] Enable Bitcode --- SyntaxKit.xcodeproj/project.pbxproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 3f91f0f..d9a8597 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -641,6 +641,7 @@ 211989B91B2EC3B600F0D786 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -662,6 +663,7 @@ 211989BA1B2EC3B600F0D786 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -733,7 +735,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_BITCODE = NO; + ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -783,7 +785,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_BITCODE = NO; + ENABLE_BITCODE = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; From 838b129312ea616cb944a5ff32ff471c00b20f5f Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 29 Jul 2016 12:41:28 +0200 Subject: [PATCH 075/110] update for Carthage and CocoaPods --- Cartfile | 1 - Cartfile.resolved | 1 - SyntaxKit.podspec | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 Cartfile delete mode 100644 Cartfile.resolved diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 05140e2..0000000 --- a/Cartfile +++ /dev/null @@ -1 +0,0 @@ -github "swift2" diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index 1fc8594..0000000 --- a/Cartfile.resolved +++ /dev/null @@ -1 +0,0 @@ -github "v0.3.1" diff --git a/SyntaxKit.podspec b/SyntaxKit.podspec index 1d827f5..3e9b0b7 100644 --- a/SyntaxKit.podspec +++ b/SyntaxKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'SyntaxKit' - spec.version = '0.1.1' + spec.version = '1.0' spec.authors = {'Sam Soffes' => 'sam@soff.es'} spec.homepage = 'https://github.com/soffes/SyntaxKit' spec.summary = 'TextMate-style syntax highlighting.' From 6c979a0e1093ea1febc3464e2deed00b9b2edca8 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 7 Aug 2016 18:04:47 +0200 Subject: [PATCH 076/110] adjust version for pr --- Readme.markdown | 2 +- SyntaxKit.podspec | 2 +- SyntaxKit/Resources/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index c50b504..0a8aa61 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -9,7 +9,7 @@ SyntaxKit was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building -SyntaxKit is written in Swift 3 so Xcode 8 is required. There aren't any dependencies besides system frameworks. +SyntaxKit is written in Swift 2 so Xcode 7 is required. There aren't any dependencies besides system frameworks. ## Installation diff --git a/SyntaxKit.podspec b/SyntaxKit.podspec index 3e9b0b7..fd3faa2 100644 --- a/SyntaxKit.podspec +++ b/SyntaxKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'SyntaxKit' - spec.version = '1.0' + spec.version = '0.9' spec.authors = {'Sam Soffes' => 'sam@soff.es'} spec.homepage = 'https://github.com/soffes/SyntaxKit' spec.summary = 'TextMate-style syntax highlighting.' diff --git a/SyntaxKit/Resources/Info.plist b/SyntaxKit/Resources/Info.plist index 98cdcda..17dee6f 100644 --- a/SyntaxKit/Resources/Info.plist +++ b/SyntaxKit/Resources/Info.plist @@ -21,7 +21,7 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) NSHumanReadableCopyright - Copyright © 2015 Sam Soffes. Copyright © 2016 Alexander Hedges. All rights reserved. + Copyright © 2015-2016 Sam Soffes. Copyright © 2016 Alexander Hedges. All rights reserved. NSPrincipalClass From c32fbe2860e68d13fc12029ce20692117f1888f2 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 27 Aug 2016 19:56:47 +0200 Subject: [PATCH 077/110] use linter on project --- SyntaxKit/AttributedParser.swift | 38 +++--- SyntaxKit/AttributedParsingOperation.swift | 72 +++++------ SyntaxKit/BundleManager.swift | 72 +++++------ SyntaxKit/Capture.swift | 10 +- SyntaxKit/CaptureCollection.swift | 22 ++-- SyntaxKit/Language.swift | 24 ++-- SyntaxKit/Parser.swift | 16 ++- SyntaxKit/Pattern.swift | 119 +++++++++--------- SyntaxKit/RefernceManager.swift | 24 ++-- SyntaxKit/Repository.swift | 20 +-- SyntaxKit/Result.swift | 12 +- SyntaxKit/ResultSet.swift | 26 ++-- SyntaxKit/ScopedString.swift | 86 ++++++------- SyntaxKit/Tests/AttributedParserTests.swift | 14 +-- SyntaxKit/Tests/IncrementalParsingTests.swift | 54 ++++---- SyntaxKit/Tests/LanguageTests.swift | 18 +-- SyntaxKit/Tests/ParserTests.swift | 2 +- SyntaxKit/Tests/ScopedStringTests.swift | 48 +++---- .../SwiftBaselineHighlightingTests.swift | 22 ++-- SyntaxKit/Tests/TestHelper.swift | 6 +- SyntaxKit/Tests/ThemeTests.swift | 14 +-- 21 files changed, 362 insertions(+), 357 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 34c641a..1b1c40f 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -13,39 +13,39 @@ import Foundation public class AttributedParser: Parser { - + // MARK: - Types - + public typealias AttributedCallback = (scope: String, range: NSRange, attributes: Attributes?) -> Void - - + + // MARK: - Properties - + public let theme: Theme - - + + // MARK: - Initializers - + public required init(language: Language, theme: Theme) { self.theme = theme super.init(language: language) } - - + + // MARK: - Parsing - + public func parse(string: String, match callback: AttributedCallback) { parse(string) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } - + func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: AttributedCallback) -> ScopedString? { return parse(incremental) { scope, range in callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) } } - + public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { let output = NSMutableAttributedString(string: string, attributes: baseAttributes) output.beginEditing() @@ -57,17 +57,17 @@ public class AttributedParser: Parser { output.endEditing() return output } - - + + // MARK: - Private - + private func attributesForScope(scope: String) -> Attributes? { let components = scope.componentsSeparatedByString(".") as NSArray let count = components.count if count == 0 { return nil } - + var attributes = Attributes() for i in 0.. Bool { @@ -44,15 +44,15 @@ struct Diff { return false } } - + return true } - + /// - returns: true if the number of characters changed is consistent with /// the new string private static func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange, oldString: NSString) -> Bool { let oldLength = insertion ? newString.length - range.length : newString.length + range.length - + if oldString.length != oldLength { return false } @@ -61,24 +61,24 @@ struct Diff { } public class AttributedParsingOperation: NSOperation { - + // MARK: - Types - + public typealias OperationCallback = [(range: NSRange, attributes: Attributes?)] -> Void - - + + // MARK: - Properties - + private let parser: AttributedParser private var operationCallback: OperationCallback private var scopedStringResult: ScopedString - + private var range: NSRange? private var diff: Diff? - - + + // MARK: - Initializers - + public init(string: String, language: Language, theme: Theme, callback: OperationCallback) { parser = AttributedParser(language: language, theme: theme) parser.string = string @@ -86,15 +86,15 @@ public class AttributedParsingOperation: NSOperation { scopedStringResult = ScopedString(string: string) super.init() } - + public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback? = nil) { parser = previousOperation.parser parser.string = string operationCallback = callback ?? previousOperation.operationCallback scopedStringResult = previousOperation.scopedStringResult - + super.init() - + let s = string as NSString let diff: Diff if insertion { @@ -102,51 +102,51 @@ public class AttributedParsingOperation: NSOperation { } else { diff = Diff(change: "", range: range) } - + if diff.representsChangesfromOldString(previousOperation.scopedStringResult.underlyingString, toNewString: string) { self.diff = diff self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range) } } - - + + // MARK: - NSOperation Implementation - + public override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] - + var incrementalParsingInfo: (NSRange, Diff, ScopedString)? if range != nil && diff != nil { incrementalParsingInfo = (range: range!, diff: diff!, previousScopes: scopedStringResult) } - + let callback = { (_: String, range: NSRange, attributes: Attributes?) in if let attributes = attributes { resultsArray.append((range, attributes)) } } - + if let result = parser.parse(incrementalParsingInfo, match: callback) { scopedStringResult = result } - + operationCallback(resultsArray) parser.string = "" } - + public override func cancel() { parser.aborted = true super.cancel() } - - + + // MARK: - Change Processing - + // Implementation notes: // If change occurred in a block reparse the lines in which the change // happened and the range of the block from this point on. If the change // occurred in the global scope just reparse the lines that changed. - + /// Returns the range in the given string that should be re-parsed after the /// given change. /// @@ -171,9 +171,9 @@ public class AttributedParsingOperation: NSOperation { if !Diff.stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: scopedStringResult.underlyingString) { return nil } - + var potentialNewString = scopedStringResult - + let linesRange: NSRange if insertion { potentialNewString.insertString(newString.substringWithRange(range), atIndex: range.location) @@ -185,7 +185,7 @@ public class AttributedParsingOperation: NSOperation { if potentialNewString.underlyingString != newString { return nil } - + let scopeAtIndex = potentialNewString.topmostScopeAtIndex(NSMaxRange(linesRange) - 1) if scopeAtIndex == potentialNewString.baseScope { return linesRange diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 2a4f290..ae83c82 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -2,9 +2,9 @@ // BundleManager.swift // SyntaxKit // -// Used to get access to SyntaxKit representations of TextMate bundle files. -// This class is used as a gateway for both internal and external use. -// Alternatively a global instace can be used for convenience. It is +// Used to get access to SyntaxKit representations of TextMate bundle files. +// This class is used as a gateway for both internal and external use. +// Alternatively a global instace can be used for convenience. It is // initialized with a callback that tells the bundle manager where to find the // files. // @@ -13,37 +13,37 @@ // public class BundleManager { - + // MARK: - Types - + /// Given an identifier of a grammar file and the format returns a url to the resource. - /// - /// - parameter identifier: The identifier of the file. Used to map it to + /// + /// - parameter identifier: The identifier of the file. Used to map it to /// the name of the file. - /// - parameter isLanguage: Whether the requested file stores a language + /// - parameter isLanguage: Whether the requested file stores a language /// (.tmLanguage) /// - returns: A URL pointing to the resource, if found public typealias BundleLocationCallback = (identifier: String, isLanguage: Bool) -> (NSURL?) - - + + // MARK: - Properties - - /// You probably want to leave the languageCaching property set to true. + + /// You probably want to leave the languageCaching property set to true. /// /// - note: Setting it to false will not invalidate or purge the cache. This /// has to be done separately using clearLanguageCache. public var languageCaching = true - + public static var defaultManager: BundleManager? - + private var bundleCallback: BundleLocationCallback private var dependencies: [Language] = [] private var cachedLanguages: [String: Language] = [:] private var cachedThemes: [String: Theme] = [:] - - + + // MARK: - Initializers - + /// Used to initialize the default manager. Unless this is called the /// defaultManager property will be set to nil. /// @@ -56,68 +56,68 @@ public class BundleManager { defaultManager!.bundleCallback = callback } } - + public init(callback: BundleLocationCallback) { self.bundleCallback = callback } - - + + // MARK: - Public - + public func languageWithIdentifier(identifier: String) -> Language? { if let language = self.cachedLanguages[identifier] { return language } - + self.dependencies = [] var language = self.getRawLanguageWithIdentifier(identifier) language?.validateWithHelperLanguages(self.dependencies) - + if languageCaching && language != nil { self.cachedLanguages[identifier] = language } - + self.dependencies = [] return language } - + public func themeWithIdentifier(identifier: String) -> Theme? { if let theme = cachedThemes[identifier] { return theme } - + guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), plist = NSDictionary(contentsOfURL: dictURL), - newTheme = Theme(dictionary: plist as [NSObject : AnyObject]) else { + newTheme = Theme(dictionary: plist as [NSObject: AnyObject]) else { return nil } - + cachedThemes[identifier] = newTheme return newTheme } - + /// Clears the language cache. Use if low on memory. public func clearLanguageCache() { self.cachedLanguages = [:] } - - + + // MARK: - Internal Interface - + /// - parameter identifier: The identifier of the requested language. /// - returns: The Language with unresolved extenal references, if found func getRawLanguageWithIdentifier(identifier: String) -> Language? { - let indexOfStoredLanguage = self.dependencies.indexOf{ (lang: Language) in lang.scopeName == identifier } - + let indexOfStoredLanguage = self.dependencies.indexOf { (lang: Language) in lang.scopeName == identifier } + if indexOfStoredLanguage != nil { return self.dependencies[indexOfStoredLanguage!] } else { guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), plist = NSDictionary(contentsOfURL: dictURL), - newLanguage = Language(dictionary: plist as [NSObject : AnyObject], bundleManager: self) else { + newLanguage = Language(dictionary: plist as [NSObject: AnyObject], bundleManager: self) else { return nil } - + self.dependencies.append(newLanguage) return newLanguage } diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index 722aadb..6e8df9a 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -9,14 +9,14 @@ // struct Capture { - + // MARK: - Properties - + let name: String - - + + // MARK: - Initializers - + init?(dictionary: [NSObject: AnyObject]) { guard let name = dictionary["name"] as? String else { return nil } self.name = name diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index f259b3b..626f165 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -9,23 +9,23 @@ // struct CaptureCollection { - + // MARK: - Properties - + private let captures: [UInt: Capture] - + var captureIndexes: [UInt] { var keys = Array(captures.keys) - keys.sortInPlace() { $0 < $1 } + keys.sortInPlace { $0 < $1 } return keys } - - + + // MARK: - Initializers - + init?(dictionary: [NSObject: AnyObject]) { guard let dictionary = dictionary as? [String: [String: String]] else { return nil } - + var captures = [UInt: Capture]() for (key, value) in dictionary { if let key = UInt(key), capture = Capture(dictionary: value) { @@ -34,10 +34,10 @@ struct CaptureCollection { } self.captures = captures } - - + + // MARK: - Accessing Captures - + subscript(index: UInt) -> Capture? { return captures[index] } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index c8fcc29..5b7fa83 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -2,7 +2,7 @@ // Language.swift // SyntaxKit // -// Represents a textmate syntax file (.tmLanguage). Before use the +// Represents a textmate syntax file (.tmLanguage). Before use the // validateWithHelperLanguages method has to be called on it. // // Created by Sam Soffes on 9/18/14. @@ -10,43 +10,43 @@ // public struct Language { - + // MARK: - Properties - + public let UUID: String // TODO: replace with uuid type in swift 3 public let name: String public let scopeName: String - + let pattern: Pattern = Pattern() let referenceManager: ReferenceManager let repository: Repository - + static let globalScope = "GLOBAL" - - + + // MARK: - Initializers - + init?(dictionary: [NSObject: AnyObject], bundleManager: BundleManager) { guard let UUID = dictionary["uuid"] as? String, name = dictionary["name"] as? String, scopeName = dictionary["scopeName"] as? String, array = dictionary["patterns"] as? [[NSObject: AnyObject]] else { return nil } - + self.UUID = UUID self.name = name self.scopeName = scopeName self.referenceManager = ReferenceManager(bundleManager: bundleManager) - + self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, withReferenceManager: referenceManager) referenceManager.resolveInternalReferences(repository, inLanguage: self) } - + /// Resolves all external reference the language has to the given languages. /// Only after a call to this method the Language is fit for general use. /// - /// - parameter helperLanguages: The languages that the language has + /// - parameter helperLanguages: The languages that the language has /// references to resolve against. This should at least contain the /// language itself. mutating func validateWithHelperLanguages(helperLanguages: [Language]) { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index bd0661f..dba3eae 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -11,7 +11,7 @@ // public class Parser { - + // MARK: - Types public typealias Callback = (scope: String, range: NSRange) -> Void @@ -35,7 +35,9 @@ public class Parser { // MARK: - Public public func parse(string: String, match callback: Callback) { - if aborted { return } + if aborted { + return + } self.string = string parse(match: callback) @@ -93,7 +95,7 @@ public class Parser { allResults.addResults(results) startIndex = NSMaxRange(results.range) if endScope != nil { - endScope = scopesString.lowerScopeForScope(endScope!, AtIndex: startIndex) + endScope = scopesString.lowerScopeForScope(endScope!, atIndex: startIndex) } } else { startIndex = endIndex @@ -108,7 +110,9 @@ public class Parser { } } - if aborted { return nil } + if aborted { + return nil + } scopesString.removeScopesInRange(allResults.range) self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) @@ -147,7 +151,9 @@ public class Parser { var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { - if aborted { return nil } + if aborted { + return nil + } let bestMatchForMiddle = findMatchFromPatterns(pattern.subpatterns, inRange: range) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 9d8c314..7a3d50d 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -21,9 +21,9 @@ @objc(SKPattern) class Pattern: NSObject { - + // MARK: - Properties - + var name: String? { return _name } var match: NSRegularExpression? { return _match } var captures: CaptureCollection? { return _captures } @@ -34,7 +34,7 @@ class Pattern: NSObject { var applyEndPatternLast: Bool { return _applyEndPatternLast} var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] - + private var _name: String? private var _match: NSRegularExpression? private var _captures: CaptureCollection? @@ -44,44 +44,44 @@ class Pattern: NSObject { private var _endCaptures: CaptureCollection? private var _applyEndPatternLast = false private weak var _parent: Pattern? - + private let debug = true - - + + // MARK: - Initializers - + init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { super.init() _parent = parent _name = dictionary["name"] as? String - + if let matchExpr = dictionary["match"] as? String { - _match = try? NSRegularExpression(pattern: matchExpr, options:[.AnchorsMatchLines]) + _match = try? NSRegularExpression(pattern: matchExpr, options: [.AnchorsMatchLines]) if debug && self.match == nil { print("Problem parsing match expression \(matchExpr)") } } - + if let beginExpr = dictionary["begin"] as? String { - _begin = try? NSRegularExpression(pattern: beginExpr, options:[.AnchorsMatchLines]) + _begin = try? NSRegularExpression(pattern: beginExpr, options: [.AnchorsMatchLines]) if debug && self.begin == nil { print("Problem parsing begin expression \(beginExpr)") } } - + if let endExpr = dictionary["end"] as? String { - _end = try? NSRegularExpression(pattern: endExpr, options:[.AnchorsMatchLines]) + _end = try? NSRegularExpression(pattern: endExpr, options: [.AnchorsMatchLines]) if debug && self.end == nil { print("Problem parsing end expression \(endExpr)") } } - + _applyEndPatternLast = dictionary["applyEndPatternLast"] as? Bool ?? false - + if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { _beginCaptures = CaptureCollection(dictionary: dictionary) } - + if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { if match != nil { _captures = CaptureCollection(dictionary: dictionary) @@ -90,17 +90,17 @@ class Pattern: NSObject { _endCaptures = self.beginCaptures } } - + if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { _endCaptures = CaptureCollection(dictionary: dictionary) } - + if dictionary["match"] as? String != nil && self.match == nil { return nil } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { return nil } - + if self.match == nil && self.begin == nil && self.end == nil && @@ -108,12 +108,12 @@ class Pattern: NSObject { print("Attention: pattern not recognized: \(self.name)") return nil } - + if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { self.subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) } } - + init(pattern: Pattern, parent: Pattern?) { super.init() _name = pattern.name @@ -126,57 +126,57 @@ class Pattern: NSObject { _parent = parent self.subpatterns = [] } - + /// For most cases does not create a usable pattern. override init() { super.init() } } -enum referenceType { - case toRepository - case toSelf - case toBase - case toForeign - case toForeignRepository - case resolved +enum ReferenceType { + case ToRepository + case ToSelf + case ToBase + case ToForeign + case ToForeignRepository + case Resolved } class Include: Pattern { - + // MARK: - Properties - - var type: referenceType {return _type} - - private var _type: referenceType + + var type: ReferenceType {return _type} + + private var _type: ReferenceType private let repositoryRef: String? private let languageRef: String? private var associatedRepository: Repository? - - + + // MARK: - Initializers - + init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?, bundleManager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { - self._type = .toRepository + self._type = .ToRepository self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) self.languageRef = nil } else if reference == "$self" { - self._type = .toSelf + self._type = .ToSelf self.repositoryRef = nil self.languageRef = nil } else if reference == "$base" { - self._type = .toBase + self._type = .ToBase self.repositoryRef = nil self.languageRef = nil } else if reference.containsString("#") { - self._type = .toForeignRepository + self._type = .ToForeignRepository self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) bundleManager.getRawLanguageWithIdentifier(languageRef!) } else { - self._type = .toForeign + self._type = .ToForeign self.repositoryRef = nil self.languageRef = reference bundleManager.getRawLanguageWithIdentifier(languageRef!) @@ -184,7 +184,7 @@ class Include: Pattern { super.init() _parent = parent } - + init(include: Include, parent: Pattern?) { self._type = include.type self.repositoryRef = include.repositoryRef @@ -192,47 +192,47 @@ class Include: Pattern { self.associatedRepository = include.associatedRepository super.init(pattern: include, parent: parent) } - - + + // MARK: - Reference Resolution - + func resolveInternalReference(repository: Repository, inLanguage language: Language) { let pattern: Pattern? - if type == .toRepository { + if type == .ToRepository { pattern = (associatedRepository ?? repository)[repositoryRef!] - } else if type == .toSelf { + } else if type == .ToSelf { pattern = language.pattern } else { return } - + if pattern != nil { self.replaceWithPattern(pattern!) } - _type = .resolved + _type = .Resolved } - + func resolveExternalReference(thisLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { let pattern: Pattern? - if type == .toBase { + if type == .ToBase { pattern = languages[baseName!]!.pattern - } else if type == .toForeignRepository { + } else if type == .ToForeignRepository { pattern = languages[languageRef!]?.repository[repositoryRef!] - } else if type == .toForeign { + } else if type == .ToForeign { pattern = languages[languageRef!]?.pattern } else { return } - + if pattern != nil { self.replaceWithPattern(pattern!) } - _type = .resolved + _type = .Resolved } - - + + // MARK: - Private - + private func replaceWithPattern(pattern: Pattern) { _name = pattern.name _match = pattern.match @@ -244,4 +244,3 @@ class Include: Pattern { self.subpatterns = pattern.subpatterns } } - diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 86aac69..97a9010 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -3,10 +3,10 @@ // SyntaxKit // // A utility class to facilitate the creation of pattern arrays. -// It works it the following fashion: First all the pattern arrays should be +// It works it the following fashion: First all the pattern arrays should be // created with patternsForArray:inRepository:caller:. Then // resolveReferencesWithRepository:inLanguage: has to be called to resolve all -// the references in the passed out patterns. So first lots of calls to +// the references in the passed out patterns. So first lots of calls to // patternsForArray and then one call to resolveReferences to validate the // patterns by resolving all references. // @@ -15,22 +15,22 @@ // class ReferenceManager { - + // MARK: - Properties - + private var includes: [Include] = [] private weak var bundleManager: BundleManager? - - + + // MARK: - Init - + init(bundleManager: BundleManager) { self.bundleManager = bundleManager } - - + + // MARK: - Pattern Creation and Resolution - + func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { var results: [Pattern] = [] for rawPattern in patterns { @@ -44,13 +44,13 @@ class ReferenceManager { } return results } - + func resolveInternalReferences(repository: Repository, inLanguage language: Language) { for include in includes { include.resolveInternalReference(repository, inLanguage: language) } } - + class func resolveExternalReferencesBetweenLanguages(languages: [Language], basename: String) { var otherLanguages: [String: Language] = [:] for language in languages { diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index 0d92a11..b2be34a 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -2,7 +2,7 @@ // Repository.swift // SyntaxKit // -// Represents a repository dictionary from a TextMate grammar. This class +// Represents a repository dictionary from a TextMate grammar. This class // supports nested repositories as found in some grammars. // // Created by Alexander Hedges on 09/01/16. @@ -10,18 +10,18 @@ // class Repository { - + // MARK: - Properties - + private var entries: [String: Pattern] = [:] private weak var parentRepository: Repository? - - + + // MARK: - Initializers - + init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, withReferenceManager refman: ReferenceManager) { self.parentRepository = parent - + for (key, value) in repo { var subRepo: Repository? if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { @@ -32,10 +32,10 @@ class Repository { } } } - - + + // MARK: - Accessing Patterns - + subscript(index: String) -> Pattern? { if let resultAtLevel = entries[index] { return resultAtLevel diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index 7d43f99..ebfd2d1 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -9,16 +9,16 @@ // struct Result: Equatable { - + // MARK: - Properties - + let patternIdentifier: String var range: NSRange let attribute: AnyObject? - - + + // MARK: - Initializers - + init(identifier: String, range: NSRange, attribute: AnyObject? = nil) { self.patternIdentifier = identifier self.range = range @@ -26,7 +26,7 @@ struct Result: Equatable { } } -func ==(lhs: Result, rhs: Result) -> Bool { +func == (lhs: Result, rhs: Result) -> Bool { return lhs.patternIdentifier == rhs.patternIdentifier && lhs.range.toRange() == rhs.range.toRange() } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 67fa395..1e81ced 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -2,43 +2,43 @@ // ResultSet.swift // SyntaxKit // -// Stores a set of results generated by the parser. +// Stores a set of results generated by the parser. // // Created by Sam Soffes on 10/11/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. // class ResultSet { - + // MARK: - Properties - + var results: [Result] { return _results } - /// Guaranteed to be larger or equal to the union of all the ranges + /// Guaranteed to be larger or equal to the union of all the ranges /// associated with the contained results. var range: NSRange { return _range } - + private var _results = [Result]() private var _range: NSRange - - + + // MARK: - Initializers - + init(startingRange range: NSRange) { _range = range } - - + + // MARK: - Adding - + func extendWithRange(range: NSRange) { _range = NSUnionRange(self.range, range) } - + func addResult(result: Result) { extendWithRange(result.range) _results.append(result) } - + func addResults(resultSet: ResultSet) { extendWithRange(resultSet.range) for result in resultSet.results { diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 490e174..63a7e97 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -2,20 +2,20 @@ // ScopedString.swift // SyntaxKit // -// A datastructure that facilitates working with strings that have nested +// A datastructure that facilitates working with strings that have nested // scopes associated with them. A scope being a named range that can have an // attribute assciated with it for the callers convenience. // The ranges can be nested. The datastructure could be visualized like this: // In fact, something like this is returned by the prettyPrint function. -// +// // Top: ---- // ------------- // ------- ----------------------------- // Bottom: ------------------------------------------ // String: "(This is) (string (with (nest)ed) scopes)!" -// +// // Note: -// In the picture above the parens are not actually in the string, they serve +// In the picture above the parens are not actually in the string, they serve // visualization purposes. The bottom-most layer is implicit and is not stored. // A new layer is added if no layer can hold the inserted scope without // creating intersections. @@ -30,32 +30,32 @@ import Foundation extension NSRange { - + func isEmpty() -> Bool { return length == 0 } - + func containsIndex(index: Int) -> Bool { return length == 0 && index == location || index >= location && index < location + length } - + func partiallyContainsRange(otherRange: NSRange) -> Bool { return otherRange.location + otherRange.length >= location && otherRange.location < location + length } - + func entirelyContainsRange(otherRange: NSRange) -> Bool { return location <= otherRange.location && location + length >= otherRange.location + otherRange.length } - + /// Removes the indexes contained in range from self and shifts itself as /// needed to not leave a gap in the domain. mutating func removeIndexesFromRange(range: NSRange) { length -= NSIntersectionRange(range, NSRange(location: location, length: length)).length - if (range.location < self.location) { + if range.location < self.location { self.location -= NSIntersectionRange(range, NSRange(location: 0, length: self.location)).length } } - + /// Inserts the indexes contained in range into self. Grows as needed. mutating func insertIndexesFromRange(range: NSRange) { if self.containsIndex(range.location) && range.location < NSMaxRange(self) { @@ -72,27 +72,27 @@ extension NSRange { typealias Scope = Result struct ScopedString { - + // MARK: - Properties - + var underlyingString: String - + private var levels: [[Scope]] = [] - + var baseScope: Scope { return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (underlyingString as NSString).length), attribute: nil) } - - + + // MARK: - Initializers - + init(string: String) { self.underlyingString = string } - - + + // MARK: - Interface - + func numberOfScopes() -> Int { var sum = 1 for level in levels { @@ -100,19 +100,19 @@ struct ScopedString { } return sum } - + func numberOfLevels() -> Int { return levels.count + 1 } - + func isInString(index: Int) -> Bool { return index >= 0 && index <= baseScope.range.length } - + mutating func addScopeAtTop(scope: Scope) { assert(scope.range.length != 0) assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) - + var added = false for level in 0.. Scope { let indexRange = NSRange(location: index, length: 0) for i in (levels.count - 1).stride(through: 0, by: -1) { @@ -153,10 +153,10 @@ struct ScopedString { } return baseScope } - - func lowerScopeForScope(scope: Scope, AtIndex index: Int) -> Scope { + + func lowerScopeForScope(scope: Scope, atIndex index: Int) -> Scope { assert(index >= 0 && index <= baseScope.range.length) - + var foundScope = false let indexRange = NSRange(location: index, length: 0) for i in (levels.count - 1).stride(through: 0, by: -1) { @@ -172,7 +172,7 @@ struct ScopedString { } return baseScope } - + func levelForScope(scope: Scope) -> Int { for i in 0 ..< levels.count { let level = levels[i] @@ -187,11 +187,11 @@ struct ScopedString { } return -1 } - + /// Removes all scopes that are entirely contained in the spcified range. mutating func removeScopesInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) - + for level in (levels.count - 1).stride(through: 0, by: -1) { for scope in (levels[level].count-1).stride(through: 0, by: -1) { let theScope = levels[level][scope] @@ -204,13 +204,13 @@ struct ScopedString { } } } - + /// Inserts the given string into the underlying string, stretching and /// shifting ranges as needed. If the range starts before and ends after the /// insertion point, it is stretched. mutating func insertString(string: String, atIndex index: Int) { assert(index >= 0 && index <= baseScope.range.length) - + let s = underlyingString as NSString let length = (string as NSString).length let mutableString = s.mutableCopy() as! NSMutableString @@ -222,12 +222,12 @@ struct ScopedString { } } } - + /// Deletes the characters from the underlying string, shrinking and /// deleting scopes as needed. mutating func deleteCharactersInRange(range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) - + let mutableString = (self.underlyingString as NSString).mutableCopy() as! NSMutableString mutableString.deleteCharactersInRange(range) self.underlyingString = mutableString.copy() as! String @@ -246,7 +246,7 @@ struct ScopedString { } } } - + /// - note: This representation is guaranteed not to change between releases /// (except for releases with breaking changes) so it can be used /// for unit testing. @@ -280,10 +280,10 @@ struct ScopedString { result += numberString + "\n" return result } - - + + // MARK: - Private - + private func findScopeIntersectionWithRange(range: NSRange, atLevel level: [Scope]) -> Scope? { for scope in level { if scope.range.partiallyContainsRange(range) { @@ -292,7 +292,7 @@ struct ScopedString { } return nil } - + private func insertionPointForRange(range: NSRange, atLevel level: [Scope]) -> Int { var i = 0 for scope in level { diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 2986f46..2e0d426 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -10,24 +10,24 @@ import XCTest import SyntaxKit class AttributedParserTests: XCTestCase { - + // MARK: - Properties - + let manager = getBundleManager() var parser: AttributedParser! - - + + // MARK: - Tests - + override func setUp() { super.setUp() let yaml = manager.languageWithIdentifier("source.YAML")! parser = AttributedParser(language: yaml, theme: simpleTheme()) } - + func testParsing() { let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") - + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(0, effectiveRange: nil) as NSDictionary) XCTAssertEqual(["color": "red"] as NSDictionary, string.attributesAtIndex(7, effectiveRange: nil) as NSDictionary) XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(19, effectiveRange: nil) as NSDictionary) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index c842d71..c5f80c9 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -10,83 +10,83 @@ import XCTest import SyntaxKit class IncrementalParsingTests: XCTestCase { - + // MARK: - Properties - + let manager = getBundleManager() var parsingOperation: AttributedParsingOperation! var theme: Theme! var language: Language! var totalRange: NSRange? var input = "" - - + + // MARK: - Tests - + override func setUp() { super.setUp() language = manager.languageWithIdentifier("Source.swift")! theme = manager.themeWithIdentifier("tomorrow")! } - + func testEdits() { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() parsingOperation.main() XCTAssertEqual(totalRange!, NSRange(location: 0, length: (input as NSString).length)) - + testInsertion("i", location: 162, expectedRange: NSRange(location: 159, length: 5)) - + testDeletion(NSRange(location: 162, length: 1), expectedRange: NSRange(location: 159, length: 4)) - + testInsertion("756", location: 160, expectedRange: NSRange(location: 159, length: 7)) } - + func testEdgeCase() { input = "// test.swift\n/**" parsingOperation = getParsingOperation() - + parsingOperation.main() XCTAssertEqual(totalRange, NSRange(location: 0, length: 17)) - + testDeletion(NSRange(location: 2, length: 1), expectedRange: NSRange(location: 0, length: 13)) testInsertion(" ", location: 2, expectedRange: NSRange(location: 0, length: 14)) - + testInsertion("\n", location: 17, expectedRange: NSRange(location: 14, length: 4)) } - + func testPerformanceInScope() { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() - + parsingOperation.main() - + self.measureBlock { self.testInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) - + self.testDeletion(NSRange(location: 239, length: 5), expectedRange: NSRange(location: 230, length: 19)) } } - + func testPerformanceEdgeCases() { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() - + parsingOperation.main() - + self.measureBlock { self.testDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) self.testInsertion("/", location: 139, expectedRange: NSRange(location: 139, length: 23)) } } - - + + // MARK: - Helpers - + private func getParsingOperation() -> AttributedParsingOperation { return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)]) in for result in results { @@ -98,20 +98,20 @@ class IncrementalParsingTests: XCTestCase { } } } - + private func testInsertion(string: String, location: Int, expectedRange expected: NSRange) { input = stringByReplacingRange(NSRange(location: location, length: 0), inString: input, withString: string) parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) - + totalRange = nil parsingOperation.main() XCTAssertEqual(totalRange!, expected) } - + private func testDeletion(range: NSRange, expectedRange expected: NSRange) { input = stringByReplacingRange(range, inString: input, withString: "") parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: false, changedRange: range) - + totalRange = nil parsingOperation.main() XCTAssertEqual(totalRange, expected) diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index a849308..572b3d8 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -10,20 +10,20 @@ import XCTest @testable import SyntaxKit class LanguageTests: XCTestCase { - + // MARK: - Properties - + let manager = getBundleManager() - - + + // MARK: - Tests - + func testYaml() { let yaml = manager.languageWithIdentifier("source.YAML")! XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) - + XCTAssertEqual("meta.embedded.line.ruby", yaml.pattern.subpatterns[0].name) XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.pattern.subpatterns[0].beginCaptures?[0]?.name) XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.pattern.subpatterns[0].endCaptures?[0]?.name) @@ -32,18 +32,18 @@ class LanguageTests: XCTestCase { XCTAssertEqual("punctuation.definition.entry.yaml", yaml.pattern.subpatterns[1].beginCaptures?[2]?.name) XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.pattern.subpatterns[1].beginCaptures?[5]?.name) XCTAssertEqual("constant.numeric.yaml", yaml.pattern.subpatterns[2].name) - + let pattern = yaml.pattern.subpatterns[3] XCTAssertEqual("string.unquoted.yaml", pattern.name) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) } - + func testSwift() { let swift = manager.languageWithIdentifier("source.swift")! XCTAssertEqual("D133338A-DEED-4ECC-9852-A392C44D10AC", swift.UUID) XCTAssertEqual("Swift", swift.name) XCTAssertEqual("source.swift", swift.scopeName) - + XCTAssertEqual("comment.line.shebang.swift", swift.pattern.subpatterns[0].name) XCTAssertEqual(4, swift.pattern.subpatterns[1].subpatterns.count) XCTAssertEqual("comment.line.double-slash.swift", swift.pattern.subpatterns[1].subpatterns[3].subpatterns[0].name) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 9b60338..81c825b 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -10,7 +10,7 @@ import XCTest import SyntaxKit class ParserTests: XCTestCase { - + // MARK: - Properties var parser: Parser! diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 6f119d8..089eda8 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -10,99 +10,99 @@ import XCTest @testable import SyntaxKit class ScopedStringTests: XCTestCase { - + override func setUp() { super.setUp() } - + override func tearDown() { super.tearDown() } - + func testScopesString() { var newScopedString = ScopedString(string: "Test") XCTAssertEqual(newScopedString.numberOfScopes(), 1) XCTAssertEqual(newScopedString.numberOfLevels(), 1) - + XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScopedString.baseScope) - + let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) newScopedString.addScopeAtTop(newScope1) // print(newScopedString.prettyRepresentation()) - + XCTAssertEqual(newScopedString.numberOfScopes(), 2) XCTAssertEqual(newScopedString.numberOfLevels(), 2) - + XCTAssertEqual(newScopedString.topmostScopeAtIndex(0), newScopedString.baseScope) XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) XCTAssertEqual(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1), newScopedString.baseScope) - + let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) newScopedString.addScopeAtTop(newScope2) // print(newScopedString.prettyRepresentation()) XCTAssertEqual(newScopedString.numberOfScopes(), 3) XCTAssertEqual(newScopedString.numberOfLevels(), 3) - + XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScope2) XCTAssertNotEqual(newScopedString.numberOfScopes(), 1) - + newScopedString.deleteCharactersInRange(NSRange(location: 2, length: 1)) // print(newScopedString.prettyRepresentation()) XCTAssertEqual(newScopedString.underlyingString, "Tet") XCTAssertEqual(newScopedString.numberOfScopes(), 2) XCTAssertEqual(newScopedString.numberOfLevels(), 2) - + XCTAssertEqual(newScopedString.topmostScopeAtIndex(1).range, NSRange(location: 1, length: 2)) - + newScopedString.insertString("ssssss", atIndex: 2) // print(newScopedString.prettyRepresentation()) XCTAssertEqual(newScopedString.underlyingString, "Tesssssst") XCTAssertEqual(newScopedString.numberOfScopes(), 2) - + newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) XCTAssertEqual(newScopedString.numberOfScopes(), 2) - + XCTAssertEqual(newScopedString.topmostScopeAtIndex(2).range, NSRange(location: 1, length: 8)) } - + func testRangeExtension() { var someRange = NSRange(location: 0, length: 24) XCTAssertFalse(someRange.isEmpty()) - + someRange = NSRange(location: 49, length: 0) XCTAssertTrue(someRange.isEmpty()) - + someRange = NSRange(location: 4, length: 2) XCTAssertTrue(someRange.containsIndex(4)) XCTAssertFalse(someRange.containsIndex(1)) XCTAssertFalse(someRange.containsIndex(23)) - + someRange = NSRange(location: 0, length: 24) someRange.removeIndexesFromRange(NSRange(location: 2, length: 4)) XCTAssertEqual(someRange, NSRange(location: 0, length: 20)) - + someRange = NSRange(location: 20, length: 40) someRange.removeIndexesFromRange(NSRange(location: 4, length: 12)) XCTAssertEqual(someRange, NSRange(location: 8, length: 40)) - + someRange = NSRange(location: 23, length: 11) someRange.removeIndexesFromRange(NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 20, length: 9)) - + someRange = NSRange(location: 10, length: 14) someRange.removeIndexesFromRange(NSRange(location: 5, length: 40)) XCTAssertTrue(someRange.isEmpty()) - + someRange = NSRange(location: 23, length: 11) someRange.insertIndexesFromRange(NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 28, length: 11)) - + someRange = NSRange(location: 14, length: 2) someRange.insertIndexesFromRange(NSRange(location: 15, length: 7)) XCTAssertEqual(someRange, NSRange(location: 14, length: 9)) - + someRange = NSRange(location: 26, length: 36) someRange.insertIndexesFromRange(NSRange(location: 62, length: 5)) XCTAssertEqual(someRange, NSRange(location: 26, length: 36)) diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 7362c43..b0309ed 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -11,34 +11,34 @@ import XCTest import SyntaxKit class SwiftBaselineHighlightingTests: XCTestCase { - + // MARK: - Properties - + let manager = getBundleManager() var parser: AttributedParser! - - + + // MARK: - Tests - + override func setUp() { super.setUp() let swift = manager.languageWithIdentifier("source.Swift")! let solarized = manager.themeWithIdentifier("Solarized")! parser = AttributedParser(language: swift, theme: solarized) } - + func testColors() { let input = fixture("swifttest.swift", "txt") let string = parser.attributedStringForString(input) - + // line comment assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - + // block comment // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - + // string literal // print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) @@ -46,14 +46,14 @@ class SwiftBaselineHighlightingTests: XCTestCase { assertEqualColors(Color(hex: "#2aa198"), string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(stringRange.length, 4) assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - + // number literal var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) assertEqualColors(Color(hex: "#d33682"), string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } - + func testHighlightingPerformance() { let input = fixture("swifttest.swift", "txt") self.measureBlock { diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 7b444d7..03c38c3 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -16,7 +16,7 @@ func fixture(name: String, _ type: String) -> String! { } func getBundleManager() -> BundleManager { - return BundleManager() { identifier, isLanguage in + return BundleManager { identifier, isLanguage in let name = isLanguage ? identifier.componentsSeparatedByString(".")[1] : identifier let ext = isLanguage ? ".tmLanguage" : ".tmTheme" return NSBundle(forClass: LanguageTests.self).URLForResource(name.capitalizedString, withExtension: ext) ?? NSURL() @@ -96,8 +96,8 @@ func assertEqualColors(color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.0 XCTAssertEqualWithAccuracy(color1!.alphaComponent, color2!.alphaComponent, accuracy: accuracy) } -extension NSRange: Equatable { } +extension NSRange: Equatable {} -public func ==(lhs: NSRange, rhs: NSRange) -> Bool { +public func == (lhs: NSRange, rhs: NSRange) -> Bool { return lhs.location == rhs.location && lhs.length == rhs.length } diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index cb93cc9..289da90 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -10,29 +10,29 @@ import XCTest @testable import SyntaxKit class ThemeTests: XCTestCase { - + // MARK: - Properties - + let manager = getBundleManager() var tomorrow: Theme! var solarized: Theme! - - + + // MARK: - Tests - + override func setUp() { super.setUp() tomorrow = manager.themeWithIdentifier("Tomorrow") solarized = manager.themeWithIdentifier("Solarized") } - + func testLoading() { XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) XCTAssertEqual("Tomorrow", tomorrow.name) assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) } - + func testComplexTheme() { XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) XCTAssertEqual("Solarized (light)", solarized.name) From ace12cf84812acbf0cdcfa7c37b27822a1242838 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 28 Aug 2016 16:36:55 +0200 Subject: [PATCH 078/110] fix more linter errors --- SyntaxKit/AttributedParser.swift | 2 +- SyntaxKit/Color.swift | 14 +++++------ SyntaxKit/Parser.swift | 4 ++-- SyntaxKit/Pattern.swift | 2 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 24 +++++++++---------- SyntaxKit/Tests/ParserTests.swift | 8 +++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 1b1c40f..5ad2478 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -70,7 +70,7 @@ public class AttributedParser: Parser { var attributes = Attributes() for i in 0.. 0 { @@ -199,7 +199,7 @@ public class Parser { /// (first criterion: matched sooner, second: higher up the list). /// /// - parameter patterns: The patterns that should be matched - /// - parameter range: The range in which the matching should happen. + /// - parameter bounds: The range in which the matching should happen. /// /// - returns: The results. nil if nothing could be matched and an empty /// set if something could be matched but it doesn't have any diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 7a3d50d..6eac23f 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -146,7 +146,7 @@ class Include: Pattern { // MARK: - Properties - var type: ReferenceType {return _type} + var type: ReferenceType {return _type} private var _type: ReferenceType private let repositoryRef: String? diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index c5f80c9..38e6c34 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -37,11 +37,11 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() XCTAssertEqual(totalRange!, NSRange(location: 0, length: (input as NSString).length)) - testInsertion("i", location: 162, expectedRange: NSRange(location: 159, length: 5)) + assertInsertion("i", location: 162, expectedRange: NSRange(location: 159, length: 5)) - testDeletion(NSRange(location: 162, length: 1), expectedRange: NSRange(location: 159, length: 4)) + assertDeletion(NSRange(location: 162, length: 1), expectedRange: NSRange(location: 159, length: 4)) - testInsertion("756", location: 160, expectedRange: NSRange(location: 159, length: 7)) + assertInsertion("756", location: 160, expectedRange: NSRange(location: 159, length: 7)) } func testEdgeCase() { @@ -51,11 +51,11 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() XCTAssertEqual(totalRange, NSRange(location: 0, length: 17)) - testDeletion(NSRange(location: 2, length: 1), expectedRange: NSRange(location: 0, length: 13)) + assertDeletion(NSRange(location: 2, length: 1), expectedRange: NSRange(location: 0, length: 13)) - testInsertion(" ", location: 2, expectedRange: NSRange(location: 0, length: 14)) + assertInsertion(" ", location: 2, expectedRange: NSRange(location: 0, length: 14)) - testInsertion("\n", location: 17, expectedRange: NSRange(location: 14, length: 4)) + assertInsertion("\n", location: 17, expectedRange: NSRange(location: 14, length: 4)) } func testPerformanceInScope() { @@ -65,9 +65,9 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() self.measureBlock { - self.testInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) + self.assertInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) - self.testDeletion(NSRange(location: 239, length: 5), expectedRange: NSRange(location: 230, length: 19)) + self.assertDeletion(NSRange(location: 239, length: 5), expectedRange: NSRange(location: 230, length: 19)) } } @@ -78,9 +78,9 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() self.measureBlock { - self.testDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) + self.assertDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) - self.testInsertion("/", location: 139, expectedRange: NSRange(location: 139, length: 23)) + self.assertInsertion("/", location: 139, expectedRange: NSRange(location: 139, length: 23)) } } @@ -99,7 +99,7 @@ class IncrementalParsingTests: XCTestCase { } } - private func testInsertion(string: String, location: Int, expectedRange expected: NSRange) { + private func assertInsertion(string: String, location: Int, expectedRange expected: NSRange) { input = stringByReplacingRange(NSRange(location: location, length: 0), inString: input, withString: string) parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) @@ -108,7 +108,7 @@ class IncrementalParsingTests: XCTestCase { XCTAssertEqual(totalRange!, expected) } - private func testDeletion(range: NSRange, expectedRange expected: NSRange) { + private func assertDeletion(range: NSRange, expectedRange expected: NSRange) { input = stringByReplacingRange(range, inString: input, withString: "") parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: false, changedRange: range) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 81c825b..e0e4cdf 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -44,9 +44,9 @@ class ParserTests: XCTestCase { } } - XCTAssertEqual(NSMakeRange(7, 13), stringQuoted) - XCTAssertEqual(NSMakeRange(7, 1), punctuationBegin) - XCTAssertEqual(NSMakeRange(19, 1), punctuationEnd) + XCTAssertEqual(NSRange(location: 7, length: 13), stringQuoted) + XCTAssertEqual(NSRange(location: 7, length: 1), punctuationBegin) + XCTAssertEqual(NSRange(location: 19, length: 1), punctuationEnd) } func testParsingBeginEndGarbage() { @@ -58,7 +58,7 @@ class ParserTests: XCTestCase { } } - XCTAssertEqual(NSMakeRange(39, 4), stringQuoted) + XCTAssertEqual(NSRange(location: 39, length: 4), stringQuoted) } func testParsingGarbage() { From e582bfe877557081e8bdb16974353105455080b5 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 28 Aug 2016 18:02:06 +0200 Subject: [PATCH 079/110] fix out-of-range bug on backspace and add test for it Closes ahedges/SyntaxKit#8 --- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 9 +++++++++ SyntaxKit/Tests/ScopedStringTests.swift | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 69f72ec..e598873 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -63,7 +63,7 @@ public class Parser { let bounds: NSRange var scopesString: ScopedString var endScope: Scope? - if incremental != nil && incremental!.previousScopes.underlyingString != (string as NSString).stringByReplacingCharactersInRange(incremental!.diff.range, withString: incremental!.diff.change) { + if incremental != nil && (incremental!.previousScopes.underlyingString as NSString).stringByReplacingCharactersInRange(incremental!.diff.range, withString: incremental!.diff.change) == string { bounds = incremental!.range scopesString = incremental!.previousScopes endScope = scopesString.topmostScopeAtIndex(bounds.location) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 38e6c34..3255558 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -44,6 +44,15 @@ class IncrementalParsingTests: XCTestCase { assertInsertion("756", location: 160, expectedRange: NSRange(location: 159, length: 7)) } + func testDeletion() { + input = "Only this!" + parsingOperation = getParsingOperation() + + parsingOperation.main() + + assertDeletion(NSRange(location: 9, length: 1), expectedRange: NSRange(location: 0,length: 9)) + } + func testEdgeCase() { input = "// test.swift\n/**" parsingOperation = getParsingOperation() diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 089eda8..af65ec2 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -35,7 +35,7 @@ class ScopedStringTests: XCTestCase { XCTAssertEqual(newScopedString.topmostScopeAtIndex(0), newScopedString.baseScope) XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) - XCTAssertEqual(newScopedString.lowerScopeForScope(newScope1, AtIndex: 1), newScopedString.baseScope) + XCTAssertEqual(newScopedString.lowerScopeForScope(newScope1, atIndex: 1), newScopedString.baseScope) let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) newScopedString.addScopeAtTop(newScope2) From f47721feb2a36cdf505d05cd54c706f9a07663ce Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 28 Aug 2016 18:43:05 +0200 Subject: [PATCH 080/110] add editorconfig --- .editorconfig | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..02cb4fb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.swift] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 From 77293125d837be65e8aabfe8e02f95a938e3ef18 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 10 Sep 2016 13:18:32 +0200 Subject: [PATCH 081/110] get rid of force casts and force unwraps --- Readme.markdown | 2 +- SyntaxKit/AttributedParsingOperation.swift | 10 +++++++++- SyntaxKit/Color.swift | 4 ++-- SyntaxKit/Parser.swift | 4 ++-- SyntaxKit/Pattern.swift | 20 +++++++++---------- SyntaxKit/ResultSet.swift | 4 ++-- SyntaxKit/ScopedString.swift | 12 +++++------ SyntaxKit/Tests/IncrementalParsingTests.swift | 2 +- SyntaxKit/Tests/TestHelper.swift | 8 ++++---- 9 files changed, 37 insertions(+), 29 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 0a8aa61..632f028 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -85,7 +85,7 @@ Easy as that. This method takes an optional `baseAttributes` parameter to custom ### Parsing Operations -There is also a `AttributedParsingOperation` subclass of NSOperation that facilitates mutithreaded parsing. +There is also a `AttributedParsingOperation` subclass of NSOperation that facilitates multithreaded parsing. ### Custom Parsers diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index d800bf9..10e0f53 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -79,6 +79,9 @@ public class AttributedParsingOperation: NSOperation { // MARK: - Initializers + /// Initializer for the first instance in the NSOperationQueue + /// + /// Can also be used if no incremental parsing is desired public init(string: String, language: Language, theme: Theme, callback: OperationCallback) { parser = AttributedParser(language: language, theme: theme) parser.string = string @@ -87,7 +90,12 @@ public class AttributedParsingOperation: NSOperation { super.init() } - public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, callback: OperationCallback? = nil) { + /// Initializer to use for operations that allow incremental parsing + /// + /// The given change has to match the change in the string between the two + /// operations. Otherwise the entire string is reparsed. If newCallback is + /// nil the callback from the previous operation will be taken. + public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, newCallback callback: OperationCallback? = nil) { parser = previousOperation.parser parser.string = string operationCallback = callback ?? previousOperation.operationCallback diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index d9f4c8f..4e2b4fe 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -23,8 +23,8 @@ public typealias Color = ColorType extension Color { - public convenience init?(hex s: String) { - var hex: NSString = s + public convenience init?(hex representation: String) { + var hex: NSString = representation // Remove `#` and `0x` if hex.hasPrefix("#") { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index e598873..acddcbd 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -82,8 +82,8 @@ public class Parser { let allResults = ResultSet(startingRange: bounds) while startIndex < endIndex { - let endPattern = endScope?.attribute as! Pattern? - guard let results = self.matchSubpatternsOfPattern(endPattern ?? language.pattern, inRange: NSRange(location: startIndex, length: endIndex - startIndex)) else { + let endPattern = endScope?.attribute as? Pattern ?? language.pattern + guard let results = self.matchSubpatternsOfPattern(endPattern, inRange: NSRange(location: startIndex, length: endIndex - startIndex)) else { return nil } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 6eac23f..c77e6ef 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -24,15 +24,15 @@ class Pattern: NSObject { // MARK: - Properties - var name: String? { return _name } - var match: NSRegularExpression? { return _match } - var captures: CaptureCollection? { return _captures } - var begin: NSRegularExpression? { return _begin } - var beginCaptures: CaptureCollection? { return _beginCaptures } - var end: NSRegularExpression? { return _end } - var endCaptures: CaptureCollection? { return _endCaptures } - var applyEndPatternLast: Bool { return _applyEndPatternLast} - var parent: Pattern? { return _parent } + var name: String? { return _name } + var match: NSRegularExpression? { return _match } + var captures: CaptureCollection? { return _captures } + var begin: NSRegularExpression? { return _begin } + var beginCaptures: CaptureCollection? { return _beginCaptures } + var end: NSRegularExpression? { return _end } + var endCaptures: CaptureCollection? { return _endCaptures } + var applyEndPatternLast: Bool { return _applyEndPatternLast} + var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] private var _name: String? @@ -104,7 +104,7 @@ class Pattern: NSObject { if self.match == nil && self.begin == nil && self.end == nil && - (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || dictionary["patterns"] as! [[NSObject: AnyObject]] == []) { + (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || (dictionary["patterns"] as? [[NSObject: AnyObject]])! == []) { print("Attention: pattern not recognized: \(self.name)") return nil } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 1e81ced..0314eeb 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -12,10 +12,10 @@ class ResultSet { // MARK: - Properties - var results: [Result] { return _results } + var results: [Result] { return _results } /// Guaranteed to be larger or equal to the union of all the ranges /// associated with the contained results. - var range: NSRange { return _range } + var range: NSRange { return _range } private var _results = [Result]() private var _range: NSRange diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 63a7e97..5c53901 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -213,9 +213,9 @@ struct ScopedString { let s = underlyingString as NSString let length = (string as NSString).length - let mutableString = s.mutableCopy() as! NSMutableString - mutableString.insertString(string, atIndex: index) - self.underlyingString = mutableString.copy() as! String + let mutableString = s.mutableCopy() as? NSMutableString + mutableString?.insertString(string, atIndex: index) + self.underlyingString = mutableString?.copy() as? String ?? "" for level in 0.. String! { let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: type)! - return try! String(contentsOfFile: path) + return try? String(contentsOfFile: path) } func getBundleManager() -> BundleManager { @@ -51,9 +51,9 @@ func simpleTheme() -> Theme! { } func stringByReplacingRange(range: NSRange, inString string: String, withString inserted: String) -> String { - let newInput = string.mutableCopy() as! NSMutableString - newInput.replaceCharactersInRange(range, withString: inserted) - return newInput.copy() as! String + let newInput = string.mutableCopy() as? NSMutableString + newInput?.replaceCharactersInRange(range, withString: inserted) + return newInput.copy() as? String ?? "" } #if !os(OSX) From 7bb4723e9e18b4d4052da15b622f19d80367decb Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 13 Sep 2016 12:34:31 +0200 Subject: [PATCH 082/110] Update to Xcode 8 and Swift 3 And while we're at it also update it to the new API Design Guidelines. Closes ahedges/SyntaxKit#3 --- SyntaxKit.xcodeproj/project.pbxproj | 37 +++- .../xcschemes/SyntaxKit-iOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-macOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-tvOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-watchOS.xcscheme | 2 +- SyntaxKit/AttributedParser.swift | 32 ++- SyntaxKit/AttributedParsingOperation.swift | 150 ++++++------- SyntaxKit/BundleManager.swift | 46 ++-- SyntaxKit/Capture.swift | 2 +- SyntaxKit/CaptureCollection.swift | 8 +- SyntaxKit/Color.swift | 28 +-- SyntaxKit/Language.swift | 29 +-- SyntaxKit/Parser.swift | 197 ++++++++---------- SyntaxKit/Pattern.swift | 108 +++++----- SyntaxKit/RefernceManager.swift | 22 +- SyntaxKit/Repository.swift | 12 +- SyntaxKit/ResultSet.swift | 14 +- SyntaxKit/ScopedString.swift | 117 +++++------ SyntaxKit/Tests/AttributedParserTests.swift | 12 +- SyntaxKit/Tests/IncrementalParsingTests.swift | 18 +- SyntaxKit/Tests/LanguageTests.swift | 8 +- SyntaxKit/Tests/ParserTests.swift | 4 +- SyntaxKit/Tests/ScopedStringTests.swift | 50 ++--- .../SwiftBaselineHighlightingTests.swift | 24 +-- SyntaxKit/Tests/TestHelper.swift | 30 +-- SyntaxKit/Tests/ThemeTests.swift | 8 +- SyntaxKit/Theme.swift | 25 +-- 27 files changed, 488 insertions(+), 501 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index c6dcb31..c650ff2 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -545,7 +545,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Sam Soffes"; TargetAttributes = { 211826D91D257A71003F2BF2 = { @@ -556,9 +556,11 @@ }; 211989A61B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; }; 211989AF1B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; }; 2122A6DD1B22B9320006409B = { CreatedOnToolsVersion = 6.3.2; @@ -826,6 +828,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -838,6 +841,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -847,6 +851,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_NONNULL = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -859,6 +864,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -876,6 +882,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit.tvos.tests; PRODUCT_NAME = SyntaxKitTests; SDKROOT = appletvos; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Debug; @@ -891,6 +898,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit.tvos.tests; PRODUCT_NAME = SyntaxKitTests; SDKROOT = appletvos; + SWIFT_VERSION = 3.0; TVOS_DEPLOYMENT_TARGET = 9.2; VALIDATE_PRODUCT = YES; }; @@ -900,7 +908,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -914,6 +922,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -922,7 +931,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -936,6 +945,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -953,6 +963,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -968,6 +979,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SWIFT_VERSION = 3.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -986,8 +998,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; @@ -1017,6 +1031,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1036,8 +1051,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; @@ -1059,6 +1076,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1067,13 +1086,13 @@ 2122A6F51B22B9320006409B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; @@ -1084,19 +1103,20 @@ PRODUCT_NAME = SyntaxKit; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; 2122A6F61B22B9320006409B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_VERSION = A; @@ -1106,6 +1126,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit; PRODUCT_NAME = SyntaxKit; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1127,6 +1148,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-macos.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -1144,6 +1166,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-macos.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -1151,6 +1174,7 @@ isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1163,6 +1187,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1172,6 +1197,7 @@ isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1184,6 +1210,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = watchos; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 2.0; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index c4fb12e..80d7e01 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ Void + public typealias AttributedCallback = (_ scope: String, _ range: NSRange, _ attributes: Attributes?) -> Void // MARK: - Properties - public let theme: Theme + open let theme: Theme // MARK: - Initializers @@ -34,20 +34,20 @@ public class AttributedParser: Parser { // MARK: - Parsing - public func parse(string: String, match callback: AttributedCallback) { + open func parse(_ string: String, match callback: AttributedCallback) { parse(string) { scope, range in - callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) + callback(scope, range, self.attributes(forScope: scope)) } } - func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: AttributedCallback) -> ScopedString? { - return parse(incremental) { scope, range in - callback(scope: scope, range: range, attributes: self.attributesForScope(scope)) + func parse(in range: NSRange?, match callback: AttributedCallback) { + parse(in: range) { scope, range in + callback(scope, range, self.attributes(forScope: scope)) } } - public func attributedStringForString(string: String, baseAttributes: Attributes? = nil) -> NSAttributedString { - let output = NSMutableAttributedString(string: string, attributes: baseAttributes) + open func attributedString(for string: String, base: Attributes? = nil) -> NSAttributedString { + let output = NSMutableAttributedString(string: string, attributes: base) output.beginEditing() parse(string) { _, range, attributes in if let attributes = attributes { @@ -61,16 +61,15 @@ public class AttributedParser: Parser { // MARK: - Private - private func attributesForScope(scope: String) -> Attributes? { - let components = scope.componentsSeparatedByString(".") as NSArray - let count = components.count - if count == 0 { + fileprivate func attributes(forScope scope: String) -> Attributes? { + let components = scope.components(separatedBy: ".") + if components.count == 0 { return nil } var attributes = Attributes() - for i in 0.. Bool { - if self.range.length == 0 { - if !Diff.stringChangeIsCompatible(newStr as String, isInsertion: true, changedRange: NSRange(location: self.range.location, length: (self.change as NSString).length), oldString: oldString) { - return false - } - if newStr.substringWithRange(NSRange(location: self.range.location, length: (self.change as NSString).length)) != self.change { - return false - } - } else { - if !Diff.stringChangeIsCompatible(newStr as String, isInsertion: false, changedRange: self.range, oldString: oldString) { - return false - } - } + // MARK: - Methods - return true + /// - returns: true if the diff represents the changes between oldString to + /// newString + func representsChanges(from oldString: String, to newString: String) -> Bool { + return newString == (oldString as NSString).replacingCharacters(in: range, with: change) + && self.change == (newString as NSString).substring(with: self.rangeInNewString()) } - /// - returns: true if the number of characters changed is consistent with - /// the new string - private static func stringChangeIsCompatible(newString: NSString, isInsertion insertion: Bool, changedRange range: NSRange, oldString: NSString) -> Bool { - let oldLength = insertion ? newString.length - range.length : newString.length + range.length + /// - returns: the range of the change in the new string + func rangeInNewString() -> NSRange { + return NSRange(location: self.range.location, length: isInsertion() ? (self.change as NSString).length : 0) + } - if oldString.length != oldLength { - return false - } - return true + /// - returns: true if the change is an insertion + func isInsertion() -> Bool { + return self.range.length == 0 } } -public class AttributedParsingOperation: NSOperation { +open class AttributedParsingOperation: Operation { // MARK: - Types - public typealias OperationCallback = [(range: NSRange, attributes: Attributes?)] -> Void + public typealias OperationCallback = ([(range: NSRange, attributes: Attributes?)]) -> Void // MARK: - Properties - private let parser: AttributedParser - private var operationCallback: OperationCallback - private var scopedStringResult: ScopedString - - private var range: NSRange? - private var diff: Diff? + fileprivate let parser: AttributedParser + fileprivate let operationCallback: OperationCallback + fileprivate var parsedRange: NSRange? // MARK: - Initializers @@ -82,67 +72,64 @@ public class AttributedParsingOperation: NSOperation { /// Initializer for the first instance in the NSOperationQueue /// /// Can also be used if no incremental parsing is desired - public init(string: String, language: Language, theme: Theme, callback: OperationCallback) { + public init(string: String, language: Language, theme: Theme, callback: @escaping OperationCallback) { parser = AttributedParser(language: language, theme: theme) - parser.string = string + parser.toParse = ScopedString(string: string) operationCallback = callback - scopedStringResult = ScopedString(string: string) super.init() } - /// Initializer to use for operations that allow incremental parsing + /// Initializer for operations that allow incremental parsing /// /// The given change has to match the change in the string between the two - /// operations. Otherwise the entire string is reparsed. If newCallback is - /// nil the callback from the previous operation will be taken. + /// operations. Otherwise the entire string will be reparsed. If newCallback + /// is nil the callback from the previous operation will be used. + /// + /// - parameter string: The new String to parse. + /// - parameter previousOperation: The preceding operation in the queue. + /// - parameter insertion: True if the change was an insertion. + /// = parameter range: Either the range in the old string that + /// was deleted or the range in the new + /// string that was added. + /// - parameter callback: The callback to call with results. public init(string: String, previousOperation: AttributedParsingOperation, changeIsInsertion insertion: Bool, changedRange range: NSRange, newCallback callback: OperationCallback? = nil) { parser = previousOperation.parser - parser.string = string operationCallback = callback ?? previousOperation.operationCallback - scopedStringResult = previousOperation.scopedStringResult super.init() - let s = string as NSString let diff: Diff if insertion { - diff = Diff(change: s.substringWithRange(range), range: NSRange(location: range.location, length: 0)) + diff = Diff(change: (string as NSString).substring(with: range), range: NSRange(location: range.location, length: 0)) } else { diff = Diff(change: "", range: range) } - if diff.representsChangesfromOldString(previousOperation.scopedStringResult.underlyingString, toNewString: string) { - self.diff = diff - self.range = outdatedRangeForChangeInString(string, changeIsInsertion: insertion, changedRange: range) + if diff.representsChanges(from: parser.toParse.string, to: string) { + self.parsedRange = AttributedParsingOperation.outdatedRange(in: string as NSString, forChange: diff, updatingPreviousResult: &self.parser.toParse) } } // MARK: - NSOperation Implementation - public override func main() { + open override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] - var incrementalParsingInfo: (NSRange, Diff, ScopedString)? - if range != nil && diff != nil { - incrementalParsingInfo = (range: range!, diff: diff!, previousScopes: scopedStringResult) - } - let callback = { (_: String, range: NSRange, attributes: Attributes?) in if let attributes = attributes { resultsArray.append((range, attributes)) } } - if let result = parser.parse(incrementalParsingInfo, match: callback) { - scopedStringResult = result - } + parser.parse(in: self.parsedRange, match: callback) - operationCallback(resultsArray) - parser.string = "" + if !parser.aborted { + operationCallback(resultsArray) + } } - public override func cancel() { + open override func cancel() { parser.aborted = true super.cancel() } @@ -162,40 +149,29 @@ public class AttributedParsingOperation: NSOperation { /// only a part of the string has to be reparsed. /// In fact passing anything other than this range to parse might lead to /// uninteded results but is not prohibited. - /// This method is only guaranteed to possibly not return nil if parse was - /// called on the old string before this call. /// - /// - parameter newString: The examined new string. Should be the product - /// of previously parsed + change. - /// - parameter insertion: Change is an insertion as opposed to a deletion. - /// - parameter range: The range in which the change occurred. In case - /// of an insertion the range in the new string that - /// was inserted. For a deletion it is the range in - /// the old string that was deleted. + /// - parameter newString: The string that will be parsed next. + /// - parameter diff: A diff representing the changes from + /// previous.string to newString. + /// - parameter previous: The result of the previous parsing pass. /// /// - returns: A range in newString that can be safely re-parsed. Or nil if /// everything has to be reparsed. - func outdatedRangeForChangeInString(newString: NSString, changeIsInsertion insertion: Bool, changedRange range: NSRange) -> NSRange? { - if !Diff.stringChangeIsCompatible(newString, isInsertion: insertion, changedRange: range, oldString: scopedStringResult.underlyingString) { - return nil - } - - var potentialNewString = scopedStringResult - + class func outdatedRange(in newString: NSString, forChange diff: Diff, updatingPreviousResult previous: inout ScopedString) -> NSRange? { let linesRange: NSRange - if insertion { - potentialNewString.insertString(newString.substringWithRange(range), atIndex: range.location) - linesRange = newString.lineRangeForRange(range) + let range: NSRange + if diff.isInsertion() { + range = diff.rangeInNewString() + previous.insert(diff.change, atIndex: range.location) + linesRange = newString.lineRange(for: range) } else { - potentialNewString.deleteCharactersInRange(range) - linesRange = newString.lineRangeForRange(NSRange(location: range.location, length: 0)) - } - if potentialNewString.underlyingString != newString { - return nil + range = diff.range + previous.deleteCharacters(in: range) + linesRange = newString.lineRange(for: NSRange(location: range.location, length: 0)) } - let scopeAtIndex = potentialNewString.topmostScopeAtIndex(NSMaxRange(linesRange) - 1) - if scopeAtIndex == potentialNewString.baseScope { + let scopeAtIndex = previous.topmostScope(atIndex: NSMaxRange(linesRange) - 1) + if scopeAtIndex == previous.baseScope { return linesRange } else { let endOfCurrentScope = NSMaxRange(scopeAtIndex.range) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index ae83c82..4dd35fc 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -12,7 +12,7 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -public class BundleManager { +open class BundleManager { // MARK: - Types @@ -23,7 +23,7 @@ public class BundleManager { /// - parameter isLanguage: Whether the requested file stores a language /// (.tmLanguage) /// - returns: A URL pointing to the resource, if found - public typealias BundleLocationCallback = (identifier: String, isLanguage: Bool) -> (NSURL?) + public typealias BundleLocationCallback = (_ identifier: String, _ isLanguage: Bool) -> (URL?) // MARK: - Properties @@ -32,14 +32,14 @@ public class BundleManager { /// /// - note: Setting it to false will not invalidate or purge the cache. This /// has to be done separately using clearLanguageCache. - public var languageCaching = true + open var languageCaching = true - public static var defaultManager: BundleManager? + open static var defaultManager: BundleManager? - private var bundleCallback: BundleLocationCallback - private var dependencies: [Language] = [] - private var cachedLanguages: [String: Language] = [:] - private var cachedThemes: [String: Theme] = [:] + fileprivate var bundleCallback: BundleLocationCallback + fileprivate var dependencies: [Language] = [] + fileprivate var cachedLanguages: [String: Language] = [:] + fileprivate var cachedThemes: [String: Theme] = [:] // MARK: - Initializers @@ -49,7 +49,7 @@ public class BundleManager { /// /// - parameter callback: The callback used to find the location of the /// textmate files. - public class func initializeDefaultManagerWithLocationCallback(callback: BundleLocationCallback) { + open class func initializeDefaultManager(with callback: @escaping BundleLocationCallback) { if defaultManager == nil { defaultManager = BundleManager(callback: callback) } else { @@ -57,21 +57,21 @@ public class BundleManager { } } - public init(callback: BundleLocationCallback) { + public init(callback: @escaping BundleLocationCallback) { self.bundleCallback = callback } // MARK: - Public - public func languageWithIdentifier(identifier: String) -> Language? { + open func language(withIdentifier identifier: String) -> Language? { if let language = self.cachedLanguages[identifier] { return language } self.dependencies = [] - var language = self.getRawLanguageWithIdentifier(identifier) - language?.validateWithHelperLanguages(self.dependencies) + var language = self.loadRawLanguage(withIdentifier: identifier) + language?.validate(with: self.dependencies) if languageCaching && language != nil { self.cachedLanguages[identifier] = language @@ -81,14 +81,14 @@ public class BundleManager { return language } - public func themeWithIdentifier(identifier: String) -> Theme? { + open func theme(withIdentifier identifier: String) -> Theme? { if let theme = cachedThemes[identifier] { return theme } - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: false), - plist = NSDictionary(contentsOfURL: dictURL), - newTheme = Theme(dictionary: plist as [NSObject: AnyObject]) else { + guard let dictURL = self.bundleCallback(identifier, false), + let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], + let newTheme = Theme(dictionary: plist) else { return nil } @@ -97,7 +97,7 @@ public class BundleManager { } /// Clears the language cache. Use if low on memory. - public func clearLanguageCache() { + open func clearLanguageCache() { self.cachedLanguages = [:] } @@ -106,15 +106,15 @@ public class BundleManager { /// - parameter identifier: The identifier of the requested language. /// - returns: The Language with unresolved extenal references, if found - func getRawLanguageWithIdentifier(identifier: String) -> Language? { - let indexOfStoredLanguage = self.dependencies.indexOf { (lang: Language) in lang.scopeName == identifier } + func loadRawLanguage(withIdentifier identifier: String) -> Language? { + let indexOfStoredLanguage = self.dependencies.index { (lang: Language) in lang.scopeName == identifier } if indexOfStoredLanguage != nil { return self.dependencies[indexOfStoredLanguage!] } else { - guard let dictURL = self.bundleCallback(identifier: identifier, isLanguage: true), - plist = NSDictionary(contentsOfURL: dictURL), - newLanguage = Language(dictionary: plist as [NSObject: AnyObject], bundleManager: self) else { + guard let dictURL = self.bundleCallback(identifier, true), + let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], + let newLanguage = Language(dictionary: plist, manager: self) else { return nil } diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index 6e8df9a..690445a 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -17,7 +17,7 @@ struct Capture { // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject]) { + init?(dictionary: [AnyHashable: Any]) { guard let name = dictionary["name"] as? String else { return nil } self.name = name } diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index 626f165..189fdf8 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -12,23 +12,23 @@ struct CaptureCollection { // MARK: - Properties - private let captures: [UInt: Capture] + fileprivate let captures: [UInt: Capture] var captureIndexes: [UInt] { var keys = Array(captures.keys) - keys.sortInPlace { $0 < $1 } + keys.sort { $0 < $1 } return keys } // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject]) { + init?(dictionary: [AnyHashable: Any]) { guard let dictionary = dictionary as? [String: [String: String]] else { return nil } var captures = [UInt: Capture]() for (key, value) in dictionary { - if let key = UInt(key), capture = Capture(dictionary: value) { + if let key = UInt(key), let capture = Capture(dictionary: value) { captures[key] = capture } } diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index 4e2b4fe..c568646 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -12,7 +12,7 @@ extension NSColor { public convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { - self.init(SRGBRed: red, green: green, blue: blue, alpha: alpha) + self.init(srgbRed: red, green: green, blue: blue, alpha: alpha) } } #else @@ -24,13 +24,13 @@ public typealias Color = ColorType extension Color { public convenience init?(hex representation: String) { - var hex: NSString = representation + var hex = representation as NSString // Remove `#` and `0x` if hex.hasPrefix("#") { - hex = hex.substringFromIndex(1) + hex = hex.substring(from: 1) as NSString } else if hex.hasPrefix("0x") { - hex = hex.substringFromIndex(2) + hex = hex.substring(from: 2) as NSString } // Invalid if not 3, 6, or 8 characters @@ -41,24 +41,24 @@ extension Color { // Make the string 8 characters long for easier parsing if length == 3 { - let r = hex.substringWithRange(NSRange(location: 0, length: 1)) - let g = hex.substringWithRange(NSRange(location: 1, length: 1)) - let b = hex.substringWithRange(NSRange(location: 2, length: 1)) - hex = r + r + g + g + b + b + "ff" + let r = hex.substring(with: NSRange(location: 0, length: 1)) + let g = hex.substring(with: NSRange(location: 1, length: 1)) + let b = hex.substring(with: NSRange(location: 2, length: 1)) + hex = "\(r)\(r)\(g)\(g)\(b)\(b)ff" as NSString } else if length == 6 { - hex = String(hex) + "ff" + hex = "\(hex)ff" as NSString } // Convert 2 character strings to CGFloats - func hexValue(string: String) -> CGFloat { + func hexValue(_ string: String) -> CGFloat { let value = Double(strtoul(string, nil, 16)) return CGFloat(value / 255.0) } - let red = hexValue(hex.substringWithRange(NSRange(location: 0, length: 2))) - let green = hexValue(hex.substringWithRange(NSRange(location: 2, length: 2))) - let blue = hexValue(hex.substringWithRange(NSRange(location: 4, length: 2))) - let alpha = hexValue(hex.substringWithRange(NSRange(location: 6, length: 2))) + let red = hexValue(hex.substring(with: NSRange(location: 0, length: 2))) + let green = hexValue(hex.substring(with: NSRange(location: 2, length: 2))) + let blue = hexValue(hex.substring(with: NSRange(location: 4, length: 2))) + let alpha = hexValue(hex.substring(with: NSRange(location: 6, length: 2))) self.init(red: red, green: green, blue: blue, alpha: alpha) } diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 5b7fa83..4f12f6f 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -3,7 +3,7 @@ // SyntaxKit // // Represents a textmate syntax file (.tmLanguage). Before use the -// validateWithHelperLanguages method has to be called on it. +// validate method has to be called on it. // // Created by Sam Soffes on 9/18/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. @@ -13,7 +13,7 @@ public struct Language { // MARK: - Properties - public let UUID: String // TODO: replace with uuid type in swift 3 + public let uuid: UUID public let name: String public let scopeName: String @@ -26,21 +26,22 @@ public struct Language { // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject], bundleManager: BundleManager) { - guard let UUID = dictionary["uuid"] as? String, - name = dictionary["name"] as? String, - scopeName = dictionary["scopeName"] as? String, - array = dictionary["patterns"] as? [[NSObject: AnyObject]] + init?(dictionary: [String: Any], manager: BundleManager) { + guard let uuidString = dictionary["uuid"] as? String, + let uuid = UUID(uuidString: uuidString), + let name = dictionary["name"] as? String, + let scopeName = dictionary["scopeName"] as? String, + let array = dictionary["patterns"] as? [[String: Any]] else { return nil } - self.UUID = UUID + self.uuid = uuid self.name = name self.scopeName = scopeName - self.referenceManager = ReferenceManager(bundleManager: bundleManager) + self.referenceManager = ReferenceManager(bundleManager: manager) - self.pattern.subpatterns = referenceManager.patternsForArray(array, inRepository: nil, caller: nil) - self.repository = Repository(repo: dictionary["repository"] as? [String: [NSObject: AnyObject]] ?? [:], inParent: nil, withReferenceManager: referenceManager) - referenceManager.resolveInternalReferences(repository, inLanguage: self) + self.pattern.subpatterns = referenceManager.patterns(for: array, in: nil, caller: nil) + self.repository = Repository(repo: dictionary["repository"] as? [String: [AnyHashable: Any]] ?? [:], inParent: nil, with: referenceManager) + referenceManager.resolveInternalReferences(with: repository, in: self) } /// Resolves all external reference the language has to the given languages. @@ -49,7 +50,7 @@ public struct Language { /// - parameter helperLanguages: The languages that the language has /// references to resolve against. This should at least contain the /// language itself. - mutating func validateWithHelperLanguages(helperLanguages: [Language]) { - ReferenceManager.resolveExternalReferencesBetweenLanguages(helperLanguages, basename: self.scopeName) + mutating func validate(with helperLanguages: [Language]) { + ReferenceManager.resolveExternalReferences(between: helperLanguages, basename: self.scopeName) } } diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index acddcbd..c3010ae 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -10,18 +10,23 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -public class Parser { +open class Parser { // MARK: - Types - public typealias Callback = (scope: String, range: NSRange) -> Void + public typealias Callback = (_ scope: String, _ range: NSRange) -> Void // MARK: - Properties - public let language: Language + /// The Language that the parser recognizes + open let language: Language - var string = "" + /// String that is used in parse(in:). May already contain lexical + /// information from previous calls to parse for incremental parsing. + /// Stores the recognized lexical scopes after a successful call to parse. + var toParse = ScopedString(string: "") + /// Set to true to abort the parsing pass var aborted = false @@ -34,89 +39,64 @@ public class Parser { // MARK: - Public - public func parse(string: String, match callback: Callback) { + open func parse(_ string: String, match callback: Callback) { if aborted { return } - - self.string = string + self.toParse = ScopedString(string: string) parse(match: callback) - self.string = "" } // MARK: - Private - /// Parses the string. Supports incremental parsing. - /// - /// The given range mey be exceeded if necessary to match a pattern entirely. + /// Parses the string in toParse. Supports incremental parsing. /// - /// - parameter incremental: A tuple containing all the information - /// necessary for incremental parsing. A range in which to parse, - /// a Diff representing the change and the result of the - /// previous call to parse. - /// - parameter callback: The callback to call on every match of a pattern - /// identifier of the language. - /// - returns: A ScopedString that contains the range results of the parsing - /// Or nil if the parsing was aborted. - func parse(incremental: (range: NSRange, diff: Diff, previousScopes: ScopedString)? = nil, match callback: Callback) -> ScopedString? { - let bounds: NSRange - var scopesString: ScopedString - var endScope: Scope? - if incremental != nil && (incremental!.previousScopes.underlyingString as NSString).stringByReplacingCharactersInRange(incremental!.diff.range, withString: incremental!.diff.change) == string { - bounds = incremental!.range - scopesString = incremental!.previousScopes - endScope = scopesString.topmostScopeAtIndex(bounds.location) - if incremental!.diff.range.length == 0 { - scopesString.insertString(incremental!.diff.change, atIndex: incremental!.diff.range.location) - } else { - scopesString.deleteCharactersInRange(incremental!.diff.range) - } - } else { - bounds = NSRange(location: 0, length: (string as NSString).length) - scopesString = ScopedString(string: string) - } - + /// - parameter range: The range that should be re-parsed or nil if the + /// entire string should be parsed. It may be exceeded + /// if necessary to match a pattern entirely. For + /// calculation of such a range take a look at + /// outdatedRange(in: forChange:). + /// - parameter match: The callback to call on every match of a pattern + /// identifier of the language. + func parse(in range: NSRange? = nil, match: Callback) { + + let bounds: NSRange = range ?? NSRange(location: 0, length: (toParse.string as NSString).length) + assert((toParse.string as NSString).length >= NSMaxRange(bounds)) + var endScope = toParse.topmostScope(atIndex: bounds.location) var startIndex = bounds.location var endIndex = NSMaxRange(bounds) let allResults = ResultSet(startingRange: bounds) while startIndex < endIndex { - let endPattern = endScope?.attribute as? Pattern ?? language.pattern - guard let results = self.matchSubpatternsOfPattern(endPattern, inRange: NSRange(location: startIndex, length: endIndex - startIndex)) else { - return nil + let endPattern = endScope.attribute as? Pattern ?? language.pattern + guard let results = self.matchSubpatterns(of: endPattern, in: NSRange(location: startIndex, length: endIndex - startIndex)) else { + return } - if endScope != nil { - allResults.addResult(Result(identifier: endScope!.patternIdentifier, range: results.range)) - } + allResults.add(Result(identifier: endScope.patternIdentifier, range: results.range)) if results.range.length != 0 { - allResults.addResults(results) + allResults.add(results) startIndex = NSMaxRange(results.range) - if endScope != nil { - endScope = scopesString.lowerScopeForScope(endScope!, atIndex: startIndex) - } + endScope = toParse.lowerScope(for: endScope, atIndex: startIndex) } else { startIndex = endIndex } - if startIndex > endIndex && scopesString.isInString(startIndex + 1) { - let scopeAtIndex = scopesString.topmostScopeAtIndex(startIndex + 1) - if endScope == nil && scopesString.levelForScope(scopeAtIndex) > 0 || - endScope != nil && scopesString.levelForScope(scopeAtIndex) > scopesString.levelForScope(endScope!) { + if startIndex > endIndex && toParse.isInString(index: startIndex + 1) { + let scopeAtIndex = toParse.topmostScope(atIndex: startIndex + 1) + if toParse.level(for: scopeAtIndex) > toParse.level(for: endScope) { endIndex = NSMaxRange(scopeAtIndex.range) } } } if aborted { - return nil + return } - - scopesString.removeScopesInRange(allResults.range) - self.applyResults(allResults, storingInScopesString: &scopesString, callback: callback) - return scopesString + toParse.removeScopes(in: allResults.range) + self.apply(allResults, callback: match) } // Algorithmic notes: @@ -135,19 +115,18 @@ public class Parser { /// Matches subpatterns of the given pattern in the input. /// /// - parameter pattern: The patterns whose subpatterns should be matched - /// - parameter bounds: The range in which the matching should occur. + /// - parameter range: The range in which the matching should occur. /// /// - returns: The result set containing the lexical scope names with range /// information or nil if aborted. May exceed range. - private func matchSubpatternsOfPattern(pattern: Pattern, inRange bounds: NSRange) -> ResultSet? { - let stop = bounds.location + bounds.length - assert((string as NSString).length >= stop) - var lineStart = bounds.location - var lineEnd = bounds.location - let result = ResultSet(startingRange: NSRange(location: bounds.location, length: 0)) + fileprivate func matchSubpatterns(of pattern: Pattern, in range: NSRange) -> ResultSet? { + let stop = range.location + range.length + var lineStart = range.location + var lineEnd = range.location + let result = ResultSet(startingRange: NSRange(location: range.location, length: 0)) while lineEnd < stop { - (string as NSString).getLineStart(nil, end: &lineEnd, contentsEnd: nil, forRange: NSRange(location: lineEnd, length: 0)) + (toParse.string as NSString).getLineStart(nil, end: &lineEnd, contentsEnd: nil, for: NSRange(location: lineEnd, length: 0)) var range = NSRange(location: lineStart, length: lineEnd - lineStart) while range.length > 0 { @@ -155,13 +134,13 @@ public class Parser { return nil } - let bestMatchForMiddle = findMatchFromPatterns(pattern.subpatterns, inRange: range) + let bestMatchForMiddle = match(pattern.subpatterns, in: range) if pattern.end != nil { - let endMatchResult = self.matchExpression(pattern.end!, inRange: range, captures: pattern.endCaptures) + let endMatchResult = self.match(pattern.end!, in: range, captures: pattern.endCaptures) if endMatchResult != nil && (bestMatchForMiddle == nil || bestMatchForMiddle != nil && (!pattern.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.match.range.location || endMatchResult!.range.location < bestMatchForMiddle!.match.range.location)) { - result.addResults(endMatchResult!) + result.add(endMatchResult!) return result } } @@ -171,14 +150,14 @@ public class Parser { if bestMatchForMiddle!.pattern.match != nil { resultForMiddle = bestMatchForMiddle!.match } else { - resultForMiddle = matchAfterBeginOfPattern(bestMatchForMiddle!.pattern, beginResults: bestMatchForMiddle!.match, inRange: range) + resultForMiddle = matchAfterBegin(of: bestMatchForMiddle!.pattern, beginResults: bestMatchForMiddle!.match) } if resultForMiddle == nil || resultForMiddle!.range.length == 0 { break } - result.addResults(resultForMiddle!) + result.add(resultForMiddle!) let newStart = NSMaxRange(resultForMiddle!.range) range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) lineEnd = max(lineEnd, newStart) @@ -190,7 +169,7 @@ public class Parser { lineStart = lineEnd } - result.extendWithRange(bounds) + result.extend(with: range) return result } @@ -198,19 +177,17 @@ public class Parser { /// them. Returns the matched pattern with the highest priority /// (first criterion: matched sooner, second: higher up the list). /// - /// - parameter patterns: The patterns that should be matched - /// - parameter bounds: The range in which the matching should happen. + /// - parameter patterns: The patterns that can be matched + /// - parameter range: The range in which the matching should happen. /// - /// - returns: The results. nil if nothing could be matched and an empty - /// set if something could be matched but it doesn't have any - /// information associated with the match. The results range may - /// exceed the passed in range. - private func findMatchFromPatterns(patterns: [Pattern], inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { - var interestingBounds = bounds + /// - returns: The matched pattern and the matching result. Nil on failure. + /// The results range may exceed the passed in range. + fileprivate func match(_ patterns: [Pattern], in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { + var interestingBounds = range var bestResult: (pattern: Pattern, match: ResultSet)? for pattern in patterns { - let currentMatch = self.firstMatchOfPattern(pattern, inRange: bounds) - if currentMatch?.match.range.location == bounds.location { + let currentMatch = self.firstMatch(of: pattern, in: range) + if currentMatch?.match.range.location == range.location { return currentMatch } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.match.range.location < bestResult!.match.range.location) { bestResult = currentMatch @@ -222,20 +199,23 @@ public class Parser { /// Matches a single pattern in the string in the given range /// - /// - returns: The result of the match. Nil if unsuccessful - private func firstMatchOfPattern(pattern: Pattern, inRange bounds: NSRange) -> (pattern: Pattern, match: ResultSet)? { - if let match = pattern.match { - if let resultSet = matchExpression(match, inRange: bounds, captures: pattern.captures, baseSelector: pattern.name) { + /// - parameter pattern: The Pattern to match in the string + /// - parameter range: The range in which to match the pattern + /// + /// - returns: The matched pattern and the matching result. Nil on failure. + fileprivate func firstMatch(of pattern: Pattern, in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { + if let expression = pattern.match { + if let resultSet = match(expression, in: range, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { return (pattern, resultSet) } } } else if let begin = pattern.begin { - if let beginResults = matchExpression(begin, inRange: bounds, captures: pattern.beginCaptures) { + if let beginResults = match(begin, in: range, captures: pattern.beginCaptures) { return (pattern, beginResults) } } else if pattern.subpatterns.count >= 1 { - return findMatchFromPatterns(pattern.subpatterns, inRange: bounds) + return match(pattern.subpatterns, in: range) } return nil } @@ -249,26 +229,24 @@ public class Parser { /// - parameter pattern: The pattern whose subpatterns and end pattern /// has to be matched /// - parameter begin: The match result of the beginning - /// - parameter range: The range in which to perform the match /// - returns: The result of matching the given pattern or nil on abortion. - /// It's range may exceed the passed in range. - private func matchAfterBeginOfPattern(pattern: Pattern, beginResults begin: ResultSet, inRange bounds: NSRange) -> ResultSet? { + fileprivate func matchAfterBegin(of pattern: Pattern, beginResults begin: ResultSet) -> ResultSet? { let newLocation = NSMaxRange(begin.range) - guard let endResults = matchSubpatternsOfPattern(pattern, inRange: NSRange(location: newLocation, length: (string as NSString).length - newLocation)) else { + guard let endResults = matchSubpatterns(of: pattern, in: NSRange(location: newLocation, length: (toParse.string as NSString).length - newLocation)) else { return nil } let result = ResultSet(startingRange: endResults.range) if pattern.name != nil { - result.addResult(Result(identifier: pattern.name!, range: NSUnionRange(begin.range, endResults.range))) + result.add(Result(identifier: pattern.name!, range: NSUnionRange(begin.range, endResults.range))) } - result.addResult(Scope(identifier: pattern.name ?? "", range: NSRange(location: begin.range.location + begin.range.length, length: NSUnionRange(begin.range, endResults.range).length - begin.range.length), attribute: pattern)) - result.addResults(begin) - result.addResults(endResults) + result.add(Scope(identifier: pattern.name ?? "", range: NSRange(location: begin.range.location + begin.range.length, length: NSUnionRange(begin.range, endResults.range).length - begin.range.length), attribute: pattern)) + result.add(begin) + result.add(endResults) return result } - /// Matches a given regular expression in a String and returns range + /// Matches a given regular expression in the String and returns range /// information for the captures /// /// - parameter expression: The regular expression to match @@ -281,29 +259,29 @@ public class Parser { /// - returns: The set containing the results. May be nil if the expression /// could not match any part of the string. It may also be empty /// and only contain range information to show what it matched. - private func matchExpression(regularExpression: NSRegularExpression, inRange bounds: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { - guard let result = regularExpression.firstMatchInString(string, options: [.WithTransparentBounds], range: bounds) else { + fileprivate func match(_ expression: NSRegularExpression, in range: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { + guard let result = expression.firstMatch(in: toParse.string, options: [.withTransparentBounds], range: range) else { return nil } let resultSet = ResultSet(startingRange: result.range) if baseSelector != nil { - resultSet.addResult(Result(identifier: baseSelector!, range: result.range)) + resultSet.add(Result(identifier: baseSelector!, range: result.range)) } if let captures = captures { for index in captures.captureIndexes { if result.numberOfRanges <= Int(index) { - print("Attention unexpected capture (\(index) to \(result.numberOfRanges)): \(regularExpression.pattern)") + print("Attention unexpected capture (\(index) to \(result.numberOfRanges)): \(expression.pattern)") continue } - let range = result.rangeAtIndex(Int(index)) + let range = result.rangeAt(Int(index)) if range.location == NSNotFound { continue } if let scope = captures[index]?.name { - resultSet.addResult(Result(identifier: scope, range: range)) + resultSet.add(Result(identifier: scope, range: range)) } } } @@ -311,19 +289,18 @@ public class Parser { return resultSet } - /// Uses the callback to communicate the result of the parsing pass back - /// to the caller of parse. + /// Uses the callback to communicate the results of the parsing pass back to + /// the caller of parse. The scopes are stored in toParse. /// - /// - parameter results: The results of the parsing pass - /// - parameter scopesString: The place to store the scopes - /// - parameter callback: The method to call on every successful match - private func applyResults(results: ResultSet, inout storingInScopesString scopesString: ScopedString, callback: Callback) { - callback(scope: Language.globalScope, range: results.range) + /// - parameter results: The results of the parsing pass + /// - parameter callback: The method to call on every successful match + fileprivate func apply(_ results: ResultSet, callback: Callback) { + callback(Language.globalScope, results.range) for result in results.results where result.range.length > 0 { if result.attribute != nil { - scopesString.addScopeAtTop(result as Scope) + toParse.addAtTop(result as Scope) } else if result.patternIdentifier != "" { - callback(scope: result.patternIdentifier, range: result.range) + callback(result.patternIdentifier, result.range) } } } diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index c77e6ef..db7dd1c 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -35,42 +35,42 @@ class Pattern: NSObject { var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] - private var _name: String? - private var _match: NSRegularExpression? - private var _captures: CaptureCollection? - private var _begin: NSRegularExpression? - private var _beginCaptures: CaptureCollection? - private var _end: NSRegularExpression? - private var _endCaptures: CaptureCollection? - private var _applyEndPatternLast = false - private weak var _parent: Pattern? + fileprivate var _name: String? + fileprivate var _match: NSRegularExpression? + fileprivate var _captures: CaptureCollection? + fileprivate var _begin: NSRegularExpression? + fileprivate var _beginCaptures: CaptureCollection? + fileprivate var _end: NSRegularExpression? + fileprivate var _endCaptures: CaptureCollection? + fileprivate var _applyEndPatternLast = false + fileprivate weak var _parent: Pattern? - private let debug = true + fileprivate let debug = true // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject], parent: Pattern?, withRepository repository: Repository?, withReferenceManager refman: ReferenceManager) { + init?(dictionary: [AnyHashable: Any], parent: Pattern?, with repository: Repository?, with manager: ReferenceManager) { super.init() _parent = parent _name = dictionary["name"] as? String if let matchExpr = dictionary["match"] as? String { - _match = try? NSRegularExpression(pattern: matchExpr, options: [.AnchorsMatchLines]) + _match = try? NSRegularExpression(pattern: matchExpr, options: [.anchorsMatchLines]) if debug && self.match == nil { print("Problem parsing match expression \(matchExpr)") } } if let beginExpr = dictionary["begin"] as? String { - _begin = try? NSRegularExpression(pattern: beginExpr, options: [.AnchorsMatchLines]) + _begin = try? NSRegularExpression(pattern: beginExpr, options: [.anchorsMatchLines]) if debug && self.begin == nil { print("Problem parsing begin expression \(beginExpr)") } } if let endExpr = dictionary["end"] as? String { - _end = try? NSRegularExpression(pattern: endExpr, options: [.AnchorsMatchLines]) + _end = try? NSRegularExpression(pattern: endExpr, options: [.anchorsMatchLines]) if debug && self.end == nil { print("Problem parsing end expression \(endExpr)") } @@ -78,11 +78,11 @@ class Pattern: NSObject { _applyEndPatternLast = dictionary["applyEndPatternLast"] as? Bool ?? false - if let dictionary = dictionary["beginCaptures"] as? [NSObject: AnyObject] { + if let dictionary = dictionary["beginCaptures"] as? [AnyHashable: Any] { _beginCaptures = CaptureCollection(dictionary: dictionary) } - if let dictionary = dictionary["captures"] as? [NSObject: AnyObject] { + if let dictionary = dictionary["captures"] as? [AnyHashable: Any] { if match != nil { _captures = CaptureCollection(dictionary: dictionary) } else if begin != nil && end != nil { @@ -91,7 +91,7 @@ class Pattern: NSObject { } } - if let dictionary = dictionary["endCaptures"] as? [NSObject: AnyObject] { + if let dictionary = dictionary["endCaptures"] as? [AnyHashable: Any] { _endCaptures = CaptureCollection(dictionary: dictionary) } @@ -104,13 +104,13 @@ class Pattern: NSObject { if self.match == nil && self.begin == nil && self.end == nil && - (dictionary["patterns"] as? [[NSObject: AnyObject]] == nil || (dictionary["patterns"] as? [[NSObject: AnyObject]])! == []) { + (dictionary["patterns"] as? [[AnyHashable: Any]] == nil || (dictionary["patterns"] as? [[AnyHashable: Any]])!.count == 0) { print("Attention: pattern not recognized: \(self.name)") return nil } - if let array = dictionary["patterns"] as? [[NSObject: AnyObject]] { - self.subpatterns = refman.patternsForArray(array, inRepository: repository, caller: self) + if let array = dictionary["patterns"] as? [[AnyHashable: Any]] { + self.subpatterns = manager.patterns(for: array, in: repository, caller: self) } } @@ -134,12 +134,12 @@ class Pattern: NSObject { } enum ReferenceType { - case ToRepository - case ToSelf - case ToBase - case ToForeign - case ToForeignRepository - case Resolved + case toRepository + case toSelf + case toBase + case toForeign + case toForeignRepository + case resolved } class Include: Pattern { @@ -148,38 +148,38 @@ class Include: Pattern { var type: ReferenceType {return _type} - private var _type: ReferenceType - private let repositoryRef: String? - private let languageRef: String? - private var associatedRepository: Repository? + fileprivate var _type: ReferenceType + fileprivate let repositoryRef: String? + fileprivate let languageRef: String? + fileprivate var associatedRepository: Repository? // MARK: - Initializers - init(reference: String, inRepository repository: Repository? = nil, parent: Pattern?, bundleManager: BundleManager) { + init(reference: String, in repository: Repository? = nil, parent: Pattern?, manager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { - self._type = .ToRepository - self.repositoryRef = reference.substringFromIndex(reference.startIndex.successor()) + self._type = .toRepository + self.repositoryRef = reference.substring(from: reference.characters.index(after: reference.startIndex)) self.languageRef = nil } else if reference == "$self" { - self._type = .ToSelf + self._type = .toSelf self.repositoryRef = nil self.languageRef = nil } else if reference == "$base" { - self._type = .ToBase + self._type = .toBase self.repositoryRef = nil self.languageRef = nil - } else if reference.containsString("#") { - self._type = .ToForeignRepository - self.repositoryRef = reference.substringFromIndex(reference.rangeOfString("#")!.endIndex) - self.languageRef = reference.substringToIndex(reference.rangeOfString("#")!.startIndex) - bundleManager.getRawLanguageWithIdentifier(languageRef!) + } else if reference.contains("#") { + self._type = .toForeignRepository + self.repositoryRef = reference.substring(from: reference.range(of: "#")!.upperBound) + self.languageRef = reference.substring(to: reference.range(of: "#")!.lowerBound) + _ = manager.loadRawLanguage(withIdentifier: languageRef!) } else { - self._type = .ToForeign + self._type = .toForeign self.repositoryRef = nil self.languageRef = reference - bundleManager.getRawLanguageWithIdentifier(languageRef!) + _ = manager.loadRawLanguage(withIdentifier: languageRef!) } super.init() _parent = parent @@ -196,44 +196,44 @@ class Include: Pattern { // MARK: - Reference Resolution - func resolveInternalReference(repository: Repository, inLanguage language: Language) { + func resolveInternalReference(with repository: Repository, in language: Language) { let pattern: Pattern? - if type == .ToRepository { + if type == .toRepository { pattern = (associatedRepository ?? repository)[repositoryRef!] - } else if type == .ToSelf { + } else if type == .toSelf { pattern = language.pattern } else { return } if pattern != nil { - self.replaceWithPattern(pattern!) + self.replace(with: pattern!) } - _type = .Resolved + _type = .resolved } - func resolveExternalReference(thisLanguage: Language, inLanguages languages: [String: Language], baseName: String?) { + func resolveExternalReference(from thisLanguage: Language, in languages: [String: Language], baseName: String?) { let pattern: Pattern? - if type == .ToBase { + if type == .toBase { pattern = languages[baseName!]!.pattern - } else if type == .ToForeignRepository { + } else if type == .toForeignRepository { pattern = languages[languageRef!]?.repository[repositoryRef!] - } else if type == .ToForeign { + } else if type == .toForeign { pattern = languages[languageRef!]?.pattern } else { return } if pattern != nil { - self.replaceWithPattern(pattern!) + self.replace(with: pattern!) } - _type = .Resolved + _type = .resolved } // MARK: - Private - private func replaceWithPattern(pattern: Pattern) { + fileprivate func replace(with pattern: Pattern) { _name = pattern.name _match = pattern.match _captures = pattern.captures diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 97a9010..7baed8b 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -4,10 +4,10 @@ // // A utility class to facilitate the creation of pattern arrays. // It works it the following fashion: First all the pattern arrays should be -// created with patternsForArray:inRepository:caller:. Then +// created with patterns:inRepository:caller:. Then // resolveReferencesWithRepository:inLanguage: has to be called to resolve all // the references in the passed out patterns. So first lots of calls to -// patternsForArray and then one call to resolveReferences to validate the +// patterns and then one call to resolveReferences to validate the // patterns by resolving all references. // // Created by Alexander Hedges on 09/01/16. @@ -18,8 +18,8 @@ class ReferenceManager { // MARK: - Properties - private var includes: [Include] = [] - private weak var bundleManager: BundleManager? + fileprivate var includes: [Include] = [] + fileprivate weak var bundleManager: BundleManager? // MARK: - Init @@ -31,27 +31,27 @@ class ReferenceManager { // MARK: - Pattern Creation and Resolution - func patternsForArray(patterns: [[NSObject: AnyObject]], inRepository repository: Repository?, caller: Pattern?) -> [Pattern] { + func patterns(for patterns: [[AnyHashable: Any]], in repository: Repository?, caller: Pattern?) -> [Pattern] { var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { - let reference = Include(reference: include, inRepository: repository, parent: caller, bundleManager: bundleManager!) + let reference = Include(reference: include, in: repository, parent: caller, manager: bundleManager!) self.includes.append(reference) results.append(reference) - } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, withRepository: repository, withReferenceManager: self) { + } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, with: repository, with: self) { results.append(pattern) } } return results } - func resolveInternalReferences(repository: Repository, inLanguage language: Language) { + func resolveInternalReferences(with repository: Repository, in language: Language) { for include in includes { - include.resolveInternalReference(repository, inLanguage: language) + include.resolveInternalReference(with: repository, in: language) } } - class func resolveExternalReferencesBetweenLanguages(languages: [Language], basename: String) { + class func resolveExternalReferences(between languages: [Language], basename: String) { var otherLanguages: [String: Language] = [:] for language in languages { otherLanguages[language.scopeName] = language @@ -59,7 +59,7 @@ class ReferenceManager { for language in languages { let includes = language.referenceManager.includes for include in includes { - include.resolveExternalReference(language, inLanguages: otherLanguages, baseName: basename) + include.resolveExternalReference(from: language, in: otherLanguages, baseName: basename) } } } diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index b2be34a..c651023 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -13,21 +13,21 @@ class Repository { // MARK: - Properties - private var entries: [String: Pattern] = [:] - private weak var parentRepository: Repository? + fileprivate var entries: [String: Pattern] = [:] + fileprivate weak var parentRepository: Repository? // MARK: - Initializers - init(repo: [String: [NSObject: AnyObject]], inParent parent: Repository?, withReferenceManager refman: ReferenceManager) { + init(repo: [String: [AnyHashable: Any]], inParent parent: Repository?, with manager: ReferenceManager) { self.parentRepository = parent for (key, value) in repo { var subRepo: Repository? - if let containedRepo = value["repository"] as? [String: [NSObject: AnyObject]] { - subRepo = Repository(repo: containedRepo, inParent: self, withReferenceManager: refman) + if let containedRepo = value["repository"] as? [String: [AnyHashable: Any]] { + subRepo = Repository(repo: containedRepo, inParent: self, with: manager) } - if let pattern = Pattern(dictionary: value, parent: nil, withRepository: subRepo, withReferenceManager: refman) { + if let pattern = Pattern(dictionary: value, parent: nil, with: subRepo, with: manager) { self.entries[key] = pattern } } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 0314eeb..69d6eac 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -17,8 +17,8 @@ class ResultSet { /// associated with the contained results. var range: NSRange { return _range } - private var _results = [Result]() - private var _range: NSRange + fileprivate var _results = [Result]() + fileprivate var _range: NSRange // MARK: - Initializers @@ -30,17 +30,17 @@ class ResultSet { // MARK: - Adding - func extendWithRange(range: NSRange) { + func extend(with range: NSRange) { _range = NSUnionRange(self.range, range) } - func addResult(result: Result) { - extendWithRange(result.range) + func add(_ result: Result) { + extend(with: result.range) _results.append(result) } - func addResults(resultSet: ResultSet) { - extendWithRange(resultSet.range) + func add(_ resultSet: ResultSet) { + extend(with: resultSet.range) for result in resultSet.results { _results.append(result) } diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 5c53901..5c148bb 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -35,21 +35,21 @@ extension NSRange { return length == 0 } - func containsIndex(index: Int) -> Bool { + func contains(index: Int) -> Bool { return length == 0 && index == location || index >= location && index < location + length } - func partiallyContainsRange(otherRange: NSRange) -> Bool { + func partiallyContains(_ otherRange: NSRange) -> Bool { return otherRange.location + otherRange.length >= location && otherRange.location < location + length } - func entirelyContainsRange(otherRange: NSRange) -> Bool { + func entirelyContains(_ otherRange: NSRange) -> Bool { return location <= otherRange.location && location + length >= otherRange.location + otherRange.length } /// Removes the indexes contained in range from self and shifts itself as /// needed to not leave a gap in the domain. - mutating func removeIndexesFromRange(range: NSRange) { + mutating func removeIndexes(from range: NSRange) { length -= NSIntersectionRange(range, NSRange(location: location, length: length)).length if range.location < self.location { self.location -= NSIntersectionRange(range, NSRange(location: 0, length: self.location)).length @@ -57,8 +57,8 @@ extension NSRange { } /// Inserts the indexes contained in range into self. Grows as needed. - mutating func insertIndexesFromRange(range: NSRange) { - if self.containsIndex(range.location) && range.location < NSMaxRange(self) { + mutating func insertIndexes(from range: NSRange) { + if self.contains(index: range.location) && range.location < NSMaxRange(self) { length += range.length } else if location > range.location { location += range.length @@ -68,26 +68,27 @@ extension NSRange { /// In this project the difference between a Result and a Scope is that the /// scope has the attribute set while the Result does not. This is an implicit -/// agreement, please respect. +/// agreement, please respect ;). typealias Scope = Result struct ScopedString { // MARK: - Properties - var underlyingString: String + var string: String - private var levels: [[Scope]] = [] + fileprivate var levels: [[Scope]] = [] + /// The inplicit scope at the base of each ScopedString var baseScope: Scope { - return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (underlyingString as NSString).length), attribute: nil) + return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (string as NSString).length), attribute: nil) } // MARK: - Initializers init(string: String) { - self.underlyingString = string + self.string = string } @@ -109,14 +110,14 @@ struct ScopedString { return index >= 0 && index <= baseScope.range.length } - mutating func addScopeAtTop(scope: Scope) { + mutating func addAtTop(_ scope: Scope) { assert(scope.range.length != 0) assert(NSIntersectionRange(scope.range, baseScope.range).length == scope.range.length) var added = false for level in 0.. Scope { + func topmostScope(atIndex index: Int) -> Scope { let indexRange = NSRange(location: index, length: 0) - for i in (levels.count - 1).stride(through: 0, by: -1) { + for i in stride(from: (levels.count - 1), through: 0, by: -1) { let level = levels[i] - if let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) { + if let theScope = findScopeIntersection(with: indexRange, at: level) { return theScope } } return baseScope } - func lowerScopeForScope(scope: Scope, atIndex index: Int) -> Scope { + func lowerScope(for scope: Scope, atIndex index: Int) -> Scope { assert(index >= 0 && index <= baseScope.range.length) var foundScope = false let indexRange = NSRange(location: index, length: 0) - for i in (levels.count - 1).stride(through: 0, by: -1) { + for i in stride(from: (levels.count - 1), through: 0, by: -1) { let level = levels[i] - let theScope = findScopeIntersectionWithRange(indexRange, atLevel: level) + let theScope = findScopeIntersection(with: indexRange, at: level) if theScope != nil { if foundScope { return scope @@ -173,7 +174,7 @@ struct ScopedString { return baseScope } - func levelForScope(scope: Scope) -> Int { + func level(for scope: Scope) -> Int { for i in 0 ..< levels.count { let level = levels[i] for currentScope in level { @@ -189,18 +190,18 @@ struct ScopedString { } /// Removes all scopes that are entirely contained in the spcified range. - mutating func removeScopesInRange(range: NSRange) { + mutating func removeScopes(in range: NSRange) { assert(NSIntersectionRange(range, baseScope.range).length == range.length) - for level in (levels.count - 1).stride(through: 0, by: -1) { - for scope in (levels[level].count-1).stride(through: 0, by: -1) { + for level in stride(from: (levels.count - 1), through: 0, by: -1) { + for scope in stride(from: (levels[level].count-1), through: 0, by: -1) { let theScope = levels[level][scope] - if range.entirelyContainsRange(theScope.range) { - levels[level].removeAtIndex(scope) + if range.entirelyContains(theScope.range) { + levels[level].remove(at: scope) } } if levels[level].count == 0 { - levels.removeAtIndex(level) + levels.remove(at: level) } } } @@ -208,41 +209,41 @@ struct ScopedString { /// Inserts the given string into the underlying string, stretching and /// shifting ranges as needed. If the range starts before and ends after the /// insertion point, it is stretched. - mutating func insertString(string: String, atIndex index: Int) { + mutating func insert(_ string: String, atIndex index: Int) { assert(index >= 0 && index <= baseScope.range.length) - let s = underlyingString as NSString + let s = self.string as NSString let length = (string as NSString).length let mutableString = s.mutableCopy() as? NSMutableString - mutableString?.insertString(string, atIndex: index) - self.underlyingString = mutableString?.copy() as? String ?? "" + mutableString?.insert(string, at: index) + self.string = mutableString?.copy() as? String ?? "" for level in 0.. String { var result = "" - var printableUnderlyingString = underlyingString.stringByReplacingOccurrencesOfString("\n", withString: "¬") - printableUnderlyingString = printableUnderlyingString.stringByReplacingOccurrencesOfString("\t", withString: "»") + var printableUnderlyingString = string.replacingOccurrences(of: "\n", with: "¬") + printableUnderlyingString = printableUnderlyingString.replacingOccurrences(of: "\t", with: "»") result += printableUnderlyingString + "\n" - for level in (levels.count - 1).stride(through: 0, by: -1) { - var levelString = String(count: (underlyingString as NSString).length, repeatedValue: " " as Character) + for level in stride(from: (levels.count - 1), through: 0, by: -1) { + var levelString = String(repeating: " ", count: (string as NSString).length) for pattern in levels[level] { let range = pattern.range if range.length == 0 { assert(false) } else if range.length == 1 { - levelString = (levelString as NSString).stringByReplacingCharactersInRange(range, withString: "|") + levelString = (levelString as NSString).replacingCharacters(in: range, with: "|") } else { - let dashes = String(count: range.length - 2, repeatedValue: "-" as Character) - levelString = (levelString as NSString).stringByReplacingCharactersInRange(range, withString: "[\(dashes)]") + let dashes = String(repeating: "-", count: range.length - 2) + levelString = (levelString as NSString).replacingCharacters(in: range, with: "[\(dashes)]") } } result += levelString + "\n" } var numberString = "" - for i in 0...(underlyingString as NSString).length/10 { + for i in 0...(string as NSString).length/10 { let numDigits = ("\(i*10)" as NSString).length - let dashes = String(count: 9 - numDigits, repeatedValue: "-" as Character) + let dashes = String(repeating: "-", count: 9 - numDigits) numberString += "\(i*10)\(dashes)|" } result += numberString + "\n" @@ -284,16 +285,16 @@ struct ScopedString { // MARK: - Private - private func findScopeIntersectionWithRange(range: NSRange, atLevel level: [Scope]) -> Scope? { + fileprivate func findScopeIntersection(with range: NSRange, at level: [Scope]) -> Scope? { for scope in level { - if scope.range.partiallyContainsRange(range) { + if scope.range.partiallyContains(range) { return scope } } return nil } - private func insertionPointForRange(range: NSRange, atLevel level: [Scope]) -> Int { + fileprivate func insertionPoint(for range: NSRange, at level: [Scope]) -> Int { var i = 0 for scope in level { if range.location < scope.range.location { diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 2e0d426..60005a3 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -21,16 +21,16 @@ class AttributedParserTests: XCTestCase { override func setUp() { super.setUp() - let yaml = manager.languageWithIdentifier("source.YAML")! + let yaml = manager.language(withIdentifier: "source.YAML")! parser = AttributedParser(language: yaml, theme: simpleTheme()) } func testParsing() { - let string = parser.attributedStringForString("title: Hello World\ncount: 42\n") + let string = parser.attributedString(for: "title: Hello World\ncount: 42\n") - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(0, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "red"] as NSDictionary, string.attributesAtIndex(7, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributesAtIndex(19, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "purple"] as NSDictionary, string.attributesAtIndex(25, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributes(at: 0, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "red"] as NSDictionary, string.attributes(at: 7, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributes(at: 19, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "purple"] as NSDictionary, string.attributes(at: 25, effectiveRange: nil) as NSDictionary) } } diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 886776c..76efd01 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -25,8 +25,8 @@ class IncrementalParsingTests: XCTestCase { override func setUp() { super.setUp() - language = manager.languageWithIdentifier("Source.swift")! - theme = manager.themeWithIdentifier("tomorrow")! + language = manager.language(withIdentifier: "Source.swift")! + theme = manager.theme(withIdentifier: "tomorrow")! } @@ -73,7 +73,7 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() - self.measureBlock { + self.measure { self.assertInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) self.assertDeletion(NSRange(location: 239, length: 5), expectedRange: NSRange(location: 230, length: 19)) @@ -86,7 +86,7 @@ class IncrementalParsingTests: XCTestCase { parsingOperation.main() - self.measureBlock { + self.measure { self.assertDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) self.assertInsertion("/", location: 139, expectedRange: NSRange(location: 139, length: 23)) @@ -96,7 +96,7 @@ class IncrementalParsingTests: XCTestCase { // MARK: - Helpers - private func getParsingOperation() -> AttributedParsingOperation { + fileprivate func getParsingOperation() -> AttributedParsingOperation { return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)]) in for result in results { if self.totalRange == nil { @@ -108,8 +108,8 @@ class IncrementalParsingTests: XCTestCase { } } - private func assertInsertion(string: String, location: Int, expectedRange expected: NSRange) { - input = stringByReplacingRange(NSRange(location: location, length: 0), inString: input, withString: string) + fileprivate func assertInsertion(_ string: String, location: Int, expectedRange expected: NSRange) { + input = replace(NSRange(location: location, length: 0), in: input, with: string) parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) totalRange = nil @@ -117,8 +117,8 @@ class IncrementalParsingTests: XCTestCase { XCTAssertEqual(totalRange!, expected) } - private func assertDeletion(range: NSRange, expectedRange expected: NSRange) { - input = stringByReplacingRange(range, inString: input, withString: "") + fileprivate func assertDeletion(_ range: NSRange, expectedRange expected: NSRange) { + input = replace(range, in: input, with: "") parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: false, changedRange: range) totalRange = nil diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 572b3d8..a51e6c7 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -19,8 +19,8 @@ class LanguageTests: XCTestCase { // MARK: - Tests func testYaml() { - let yaml = manager.languageWithIdentifier("source.YAML")! - XCTAssertEqual("B0C44228-4F1F-11DA-AFF2-000A95AF0064", yaml.UUID) + let yaml = manager.language(withIdentifier: "source.YAML")! + XCTAssertEqual(UUID(uuidString: "B0C44228-4F1F-11DA-AFF2-000A95AF0064"), yaml.uuid) XCTAssertEqual("YAML", yaml.name) XCTAssertEqual("source.yaml", yaml.scopeName) @@ -39,8 +39,8 @@ class LanguageTests: XCTestCase { } func testSwift() { - let swift = manager.languageWithIdentifier("source.swift")! - XCTAssertEqual("D133338A-DEED-4ECC-9852-A392C44D10AC", swift.UUID) + let swift = manager.language(withIdentifier: "source.swift")! + XCTAssertEqual(UUID(uuidString: "D133338A-DEED-4ECC-9852-A392C44D10AC"), swift.uuid) XCTAssertEqual("Swift", swift.name) XCTAssertEqual("source.swift", swift.scopeName) diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index e0e4cdf..a6b6de5 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -20,7 +20,7 @@ class ParserTests: XCTestCase { override func setUp() { super.setUp() - let yaml = manager.languageWithIdentifier("source.YAML")! + let yaml = manager.language(withIdentifier: "source.YAML")! parser = Parser(language: yaml) } @@ -67,7 +67,7 @@ class ParserTests: XCTestCase { } func testRuby() { - let ruby = manager.languageWithIdentifier("source.Ruby")! + let ruby = manager.language(withIdentifier: "source.Ruby")! parser = Parser(language: ruby) let input = fixture("test.rb", "txt") parser.parse(input, match: { _ in return }) diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index af65ec2..4c94028 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -24,47 +24,47 @@ class ScopedStringTests: XCTestCase { XCTAssertEqual(newScopedString.numberOfScopes(), 1) XCTAssertEqual(newScopedString.numberOfLevels(), 1) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 2), newScopedString.baseScope) let newScope1 = Scope(identifier: "bogus", range: NSRange(location: 1, length: 3), attribute: nil) - newScopedString.addScopeAtTop(newScope1) + newScopedString.addAtTop(newScope1) // print(newScopedString.prettyRepresentation()) XCTAssertEqual(newScopedString.numberOfScopes(), 2) XCTAssertEqual(newScopedString.numberOfLevels(), 2) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(0), newScopedString.baseScope) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) - XCTAssertEqual(newScopedString.lowerScopeForScope(newScope1, atIndex: 1), newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 0), newScopedString.baseScope) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 1), newScope1) + XCTAssertEqual(newScopedString.lowerScope(for: newScope1, atIndex: 1), newScopedString.baseScope) let newScope2 = Scope(identifier: "bogus2", range: NSRange(location: 2, length: 1), attribute: nil) - newScopedString.addScopeAtTop(newScope2) + newScopedString.addAtTop(newScope2) // print(newScopedString.prettyRepresentation()) XCTAssertEqual(newScopedString.numberOfScopes(), 3) XCTAssertEqual(newScopedString.numberOfLevels(), 3) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(1), newScope1) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(2), newScope2) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 1), newScope1) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 2), newScope2) XCTAssertNotEqual(newScopedString.numberOfScopes(), 1) - newScopedString.deleteCharactersInRange(NSRange(location: 2, length: 1)) + newScopedString.deleteCharacters(in: NSRange(location: 2, length: 1)) // print(newScopedString.prettyRepresentation()) - XCTAssertEqual(newScopedString.underlyingString, "Tet") + XCTAssertEqual(newScopedString.string, "Tet") XCTAssertEqual(newScopedString.numberOfScopes(), 2) XCTAssertEqual(newScopedString.numberOfLevels(), 2) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(1).range, NSRange(location: 1, length: 2)) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 1).range, NSRange(location: 1, length: 2)) - newScopedString.insertString("ssssss", atIndex: 2) + newScopedString.insert("ssssss", atIndex: 2) // print(newScopedString.prettyRepresentation()) - XCTAssertEqual(newScopedString.underlyingString, "Tesssssst") + XCTAssertEqual(newScopedString.string, "Tesssssst") XCTAssertEqual(newScopedString.numberOfScopes(), 2) - newScopedString.removeScopesInRange(NSRange(location: 0, length: 1)) + newScopedString.removeScopes(in: NSRange(location: 0, length: 1)) XCTAssertEqual(newScopedString.numberOfScopes(), 2) - XCTAssertEqual(newScopedString.topmostScopeAtIndex(2).range, NSRange(location: 1, length: 8)) + XCTAssertEqual(newScopedString.topmostScope(atIndex: 2).range, NSRange(location: 1, length: 8)) } func testRangeExtension() { @@ -75,36 +75,36 @@ class ScopedStringTests: XCTestCase { XCTAssertTrue(someRange.isEmpty()) someRange = NSRange(location: 4, length: 2) - XCTAssertTrue(someRange.containsIndex(4)) - XCTAssertFalse(someRange.containsIndex(1)) - XCTAssertFalse(someRange.containsIndex(23)) + XCTAssertTrue(someRange.contains(index: 4)) + XCTAssertFalse(someRange.contains(index: 1)) + XCTAssertFalse(someRange.contains(index: 23)) someRange = NSRange(location: 0, length: 24) - someRange.removeIndexesFromRange(NSRange(location: 2, length: 4)) + someRange.removeIndexes(from: NSRange(location: 2, length: 4)) XCTAssertEqual(someRange, NSRange(location: 0, length: 20)) someRange = NSRange(location: 20, length: 40) - someRange.removeIndexesFromRange(NSRange(location: 4, length: 12)) + someRange.removeIndexes(from: NSRange(location: 4, length: 12)) XCTAssertEqual(someRange, NSRange(location: 8, length: 40)) someRange = NSRange(location: 23, length: 11) - someRange.removeIndexesFromRange(NSRange(location: 20, length: 5)) + someRange.removeIndexes(from: NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 20, length: 9)) someRange = NSRange(location: 10, length: 14) - someRange.removeIndexesFromRange(NSRange(location: 5, length: 40)) + someRange.removeIndexes(from: NSRange(location: 5, length: 40)) XCTAssertTrue(someRange.isEmpty()) someRange = NSRange(location: 23, length: 11) - someRange.insertIndexesFromRange(NSRange(location: 20, length: 5)) + someRange.insertIndexes(from: NSRange(location: 20, length: 5)) XCTAssertEqual(someRange, NSRange(location: 28, length: 11)) someRange = NSRange(location: 14, length: 2) - someRange.insertIndexesFromRange(NSRange(location: 15, length: 7)) + someRange.insertIndexes(from: NSRange(location: 15, length: 7)) XCTAssertEqual(someRange, NSRange(location: 14, length: 9)) someRange = NSRange(location: 26, length: 36) - someRange.insertIndexesFromRange(NSRange(location: 62, length: 5)) + someRange.insertIndexes(from: NSRange(location: 62, length: 5)) XCTAssertEqual(someRange, NSRange(location: 26, length: 36)) } } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index b0309ed..2bda05b 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -22,42 +22,42 @@ class SwiftBaselineHighlightingTests: XCTestCase { override func setUp() { super.setUp() - let swift = manager.languageWithIdentifier("source.Swift")! - let solarized = manager.themeWithIdentifier("Solarized")! + let swift = manager.language(withIdentifier: "source.Swift")! + let solarized = manager.theme(withIdentifier: "Solarized")! parser = AttributedParser(language: swift, theme: solarized) } func testColors() { let input = fixture("swifttest.swift", "txt") - let string = parser.attributedStringForString(input) + let string = parser.attributedString(for: input) // line comment - assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // block comment // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) - assertEqualColors(Color(hex: "#93A1A1"), string.attributesAtIndex(157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // string literal // print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) - assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) var stringRange = NSRange() - assertEqualColors(Color(hex: "#2aa198"), string.attributesAtIndex(745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(stringRange.length, 4) - assertEqualColors(Color(hex: "#839496"), string.attributesAtIndex(749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) // number literal var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) - assertEqualColors(Color(hex: "#d33682"), string.attributesAtIndex(715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } func testHighlightingPerformance() { let input = fixture("swifttest.swift", "txt") - self.measureBlock { - self.parser.attributedStringForString(input) + self.measure { + _ = self.parser.attributedString(for: input) } } } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index acfa586..7cde91b 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -10,22 +10,28 @@ import Foundation import XCTest @testable import SyntaxKit -func fixture(name: String, _ type: String) -> String! { - let path = NSBundle(forClass: LanguageTests.self).pathForResource(name, ofType: type)! - return try? String(contentsOfFile: path) +func fixture(_ name: String, _ type: String) -> String { + if let path = Bundle(for: LanguageTests.self).path(forResource: name, ofType: type) { + do { + return try String(contentsOfFile: path) + } catch { + return "" + } + } + return "" } func getBundleManager() -> BundleManager { return BundleManager { identifier, isLanguage in - let name = isLanguage ? identifier.componentsSeparatedByString(".")[1] : identifier + let name = isLanguage ? identifier._split(separator: ".")[1] : identifier let ext = isLanguage ? ".tmLanguage" : ".tmTheme" - return NSBundle(forClass: LanguageTests.self).URLForResource(name.capitalizedString, withExtension: ext) ?? NSURL() + return Bundle(for: LanguageTests.self).url(forResource: name.capitalized, withExtension: ext) ?? URL(fileURLWithPath: "") } } -func simpleTheme() -> Theme! { +func simpleTheme() -> Theme { return Theme(dictionary: [ - "uuid": "7", + "uuid": "123e4567-e89b-12d3-a456-426655440000", "name": "Simple", "settings": [ [ @@ -47,13 +53,13 @@ func simpleTheme() -> Theme! { ] ] ] - ]) + ])! } -func stringByReplacingRange(range: NSRange, inString string: String, withString inserted: String) -> String { +func replace(_ range: NSRange, in string: String, with inserted: String) -> String { let newInput = string.mutableCopy() as? NSMutableString - newInput?.replaceCharactersInRange(range, withString: inserted) - return newInput.copy() as? String ?? "" + newInput?.replaceCharacters(in: range, with: inserted) + return newInput!.copy() as? String ?? "" } #if !os(OSX) @@ -85,7 +91,7 @@ extension Color { } #endif -func assertEqualColors(color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.005) { +func assertEqualColors(_ color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.005) { if color1 == nil || color2 == nil { XCTAssert(false, "colors have to be non-nil") return diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 289da90..9327427 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -22,19 +22,19 @@ class ThemeTests: XCTestCase { override func setUp() { super.setUp() - tomorrow = manager.themeWithIdentifier("Tomorrow") - solarized = manager.themeWithIdentifier("Solarized") + tomorrow = manager.theme(withIdentifier: "Tomorrow") + solarized = manager.theme(withIdentifier: "Solarized") } func testLoading() { - XCTAssertEqual("82CCD69C-F1B1-4529-B39E-780F91F07604", tomorrow.UUID) + XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) XCTAssertEqual("Tomorrow", tomorrow.name) assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) } func testComplexTheme() { - XCTAssertEqual("38E819D9-AE02-452F-9231-ECC3B204AFD7", solarized.UUID) + XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) XCTAssertEqual("Solarized (light)", solarized.name) assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as? Color) assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as? Color) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index b9e7aac..909b169 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -21,47 +21,48 @@ public struct Theme { // MARK: - Properties - public let UUID: String // TODO: replace with uuid type in swift 3 + public let uuid: UUID public let name: String public let attributes: [String: Attributes] public var backgroundColor: Color { - return attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color ?? Color.whiteColor() + return attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color ?? Color.white } public var foregroundColor: Color { - return attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color ?? Color.blackColor() + return attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color ?? Color.black } // MARK: - Initializers - init?(dictionary: [NSObject: AnyObject]) { - guard let UUID = dictionary["uuid"] as? String, - name = dictionary["name"] as? String, - rawSettings = dictionary["settings"] as? [[String: AnyObject]] + init?(dictionary: [String: Any]) { + guard let uuidString = dictionary["uuid"] as? String, + let uuid = UUID(uuidString: uuidString), + let name = dictionary["name"] as? String, + let rawSettings = dictionary["settings"] as? [[String: AnyObject]] else { return nil } - self.UUID = UUID + self.uuid = uuid self.name = name var attributes = [String: Attributes]() for raw in rawSettings { guard var setting = raw["settings"] as? [String: AnyObject] else { continue } - if let value = setting.removeValueForKey("foreground") as? String { + if let value = setting.removeValue(forKey: "foreground") as? String { setting[NSForegroundColorAttributeName] = Color(hex: value) } - if let value = setting.removeValueForKey("background") as? String { + if let value = setting.removeValue(forKey: "background") as? String { setting[NSBackgroundColorAttributeName] = Color(hex: value) } // TODO: caret, invisibles, lightHighlight, selection, font style if let patternIdentifiers = raw["scope"] as? String { - for patternIdentifier in patternIdentifiers.componentsSeparatedByString(",") { - let key = patternIdentifier.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + for patternIdentifier in patternIdentifiers.components(separatedBy: ",") { + let key = patternIdentifier.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) attributes[key] = setting } } else if !setting.isEmpty { From d23de4fc5c7ca47bae37d49e0c3a356fd1e82624 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 13 Sep 2016 12:43:07 +0200 Subject: [PATCH 083/110] update readme and small fix --- Readme.markdown | 8 ++++---- SyntaxKit/AttributedParsingOperation.swift | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 632f028..aef0ee4 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -9,7 +9,7 @@ SyntaxKit was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building -SyntaxKit is written in Swift 2 so Xcode 7 is required. There aren't any dependencies besides system frameworks. +SyntaxKit is written in Swift 3 so Xcode 8 is required. There aren't any dependencies besides system frameworks. ## Installation @@ -43,7 +43,7 @@ import SyntaxKit let manager = BundleManager() { identifier, isLanguage in NSURL(string: "Location of Bundles/" + identifier + ".plist") } -let yaml = manager.languageWithIdentifier("source.yaml")! +let yaml = manager.language(withIdentifier: "source.yaml")! let parser = Parser(language: yaml) ``` @@ -65,7 +65,7 @@ parser.parse(input) { scope, range in SyntaxKit also comes with `AttributedParser`. This is a simple subclass of `Parser` that knows how to work with themes. ```swift -let tomorrow = manager.themeWithIdentifier("tomorrow")! +let tomorrow = manager.theme(withIdentifier: "tomorrow")! let attributedParser = AttributedParser(language: yaml, theme: tomorrow) attributedParser.parse(input) { scope, range, attributes in @@ -78,7 +78,7 @@ Notice that `attributes` is the third parameter to the block now. This is a dict `AttributedParser` includes a convenience method for turning a `String` of source code into an `NSAttributedString`: ```swift -let attributedString = attributedParser.attributedStringForString(input) +let attributedString = attributedParser.attributedString(for: input) ``` Easy as that. This method takes an optional `baseAttributes` parameter to customize how the string is created. This is great if you want to specify a font, etc. diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index df21558..4634573 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -107,6 +107,8 @@ open class AttributedParsingOperation: Operation { if diff.representsChanges(from: parser.toParse.string, to: string) { self.parsedRange = AttributedParsingOperation.outdatedRange(in: string as NSString, forChange: diff, updatingPreviousResult: &self.parser.toParse) + } else { + self.parser.toParse = ScopedString(string: string) } } @@ -115,7 +117,6 @@ open class AttributedParsingOperation: Operation { open override func main() { var resultsArray: [(range: NSRange, attributes: Attributes?)] = [] - let callback = { (_: String, range: NSRange, attributes: Attributes?) in if let attributes = attributes { resultsArray.append((range, attributes)) From 46004b31332e3583fa4458fd7b0b39e7b2b746ab Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 13 Sep 2016 14:41:27 +0200 Subject: [PATCH 084/110] bump version to 1.0 --- SyntaxKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit.podspec b/SyntaxKit.podspec index fd3faa2..3e9b0b7 100644 --- a/SyntaxKit.podspec +++ b/SyntaxKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'SyntaxKit' - spec.version = '0.9' + spec.version = '1.0' spec.authors = {'Sam Soffes' => 'sam@soff.es'} spec.homepage = 'https://github.com/soffes/SyntaxKit' spec.summary = 'TextMate-style syntax highlighting.' From da4e922bd80b1657c07f19da667976eb40ca0d49 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 24 Sep 2016 21:32:59 +0200 Subject: [PATCH 085/110] Pass sender into the operation callback --- SyntaxKit/AttributedParsingOperation.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index 4634573..877776b 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -57,7 +57,11 @@ open class AttributedParsingOperation: Operation { // MARK: - Types - public typealias OperationCallback = ([(range: NSRange, attributes: Attributes?)]) -> Void + /// Asynchronous or synchronous callback to which the results are passed + /// + /// The sender is passed in so it can be used to check if the operation was + /// cancelled after the call. + public typealias OperationCallback = ([(range: NSRange, attributes: Attributes?)], AttributedParsingOperation) -> Void // MARK: - Properties @@ -88,7 +92,7 @@ open class AttributedParsingOperation: Operation { /// - parameter string: The new String to parse. /// - parameter previousOperation: The preceding operation in the queue. /// - parameter insertion: True if the change was an insertion. - /// = parameter range: Either the range in the old string that + /// - parameter range: Either the range in the old string that /// was deleted or the range in the new /// string that was added. /// - parameter callback: The callback to call with results. @@ -126,7 +130,7 @@ open class AttributedParsingOperation: Operation { parser.parse(in: self.parsedRange, match: callback) if !parser.aborted { - operationCallback(resultsArray) + operationCallback(resultsArray, self) } } From 54bf2a238431349b7009603c365b7d4d07dd1620 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 24 Sep 2016 21:41:28 +0200 Subject: [PATCH 086/110] fix tests --- SyntaxKit/Tests/IncrementalParsingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 76efd01..4c7414e 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -97,7 +97,7 @@ class IncrementalParsingTests: XCTestCase { // MARK: - Helpers fileprivate func getParsingOperation() -> AttributedParsingOperation { - return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)]) in + return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], sender: AttributedParsingOperation) in for result in results { if self.totalRange == nil { self.totalRange = result.range From de1b88026f09603838dc8aff7b969ff670d42046 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 6 Nov 2016 13:40:02 +0100 Subject: [PATCH 087/110] follow linter suggestions --- SyntaxKit/AttributedParser.swift | 6 +----- SyntaxKit/AttributedParsingOperation.swift | 5 ----- SyntaxKit/BundleManager.swift | 4 ---- SyntaxKit/Capture.swift | 6 ++++-- SyntaxKit/CaptureCollection.swift | 6 +++--- SyntaxKit/Language.swift | 1 - SyntaxKit/Parser.swift | 4 ---- SyntaxKit/Pattern.swift | 10 +++------- SyntaxKit/RefernceManager.swift | 2 -- SyntaxKit/Repository.swift | 2 -- SyntaxKit/Result.swift | 1 - SyntaxKit/ResultSet.swift | 2 -- SyntaxKit/ScopedString.swift | 7 ++----- SyntaxKit/Tests/AttributedParserTests.swift | 1 - SyntaxKit/Tests/IncrementalParsingTests.swift | 2 -- SyntaxKit/Tests/LanguageTests.swift | 1 - SyntaxKit/Tests/ParserTests.swift | 1 - SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift | 1 - SyntaxKit/Tests/ThemeTests.swift | 1 - SyntaxKit/Theme.swift | 1 - 20 files changed, 13 insertions(+), 51 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index d79d82a..bdec3cc 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -18,12 +18,10 @@ open class AttributedParser: Parser { public typealias AttributedCallback = (_ scope: String, _ range: NSRange, _ attributes: Attributes?) -> Void - // MARK: - Properties open let theme: Theme - // MARK: - Initializers public required init(language: Language, theme: Theme) { @@ -31,7 +29,6 @@ open class AttributedParser: Parser { super.init(language: language) } - // MARK: - Parsing open func parse(_ string: String, match callback: AttributedCallback) { @@ -58,12 +55,11 @@ open class AttributedParser: Parser { return output } - // MARK: - Private fileprivate func attributes(forScope scope: String) -> Attributes? { let components = scope.components(separatedBy: ".") - if components.count == 0 { + if components.isEmpty { return nil } diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index 877776b..e942cd5 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -32,7 +32,6 @@ struct Diff { /// - Deletion: The range of deleted characters var range: NSRange - // MARK: - Methods /// - returns: true if the diff represents the changes between oldString to @@ -63,14 +62,12 @@ open class AttributedParsingOperation: Operation { /// cancelled after the call. public typealias OperationCallback = ([(range: NSRange, attributes: Attributes?)], AttributedParsingOperation) -> Void - // MARK: - Properties fileprivate let parser: AttributedParser fileprivate let operationCallback: OperationCallback fileprivate var parsedRange: NSRange? - // MARK: - Initializers /// Initializer for the first instance in the NSOperationQueue @@ -116,7 +113,6 @@ open class AttributedParsingOperation: Operation { } } - // MARK: - NSOperation Implementation open override func main() { @@ -139,7 +135,6 @@ open class AttributedParsingOperation: Operation { super.cancel() } - // MARK: - Change Processing // Implementation notes: diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 4dd35fc..6097490 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -25,7 +25,6 @@ open class BundleManager { /// - returns: A URL pointing to the resource, if found public typealias BundleLocationCallback = (_ identifier: String, _ isLanguage: Bool) -> (URL?) - // MARK: - Properties /// You probably want to leave the languageCaching property set to true. @@ -41,7 +40,6 @@ open class BundleManager { fileprivate var cachedLanguages: [String: Language] = [:] fileprivate var cachedThemes: [String: Theme] = [:] - // MARK: - Initializers /// Used to initialize the default manager. Unless this is called the @@ -61,7 +59,6 @@ open class BundleManager { self.bundleCallback = callback } - // MARK: - Public open func language(withIdentifier identifier: String) -> Language? { @@ -101,7 +98,6 @@ open class BundleManager { self.cachedLanguages = [:] } - // MARK: - Internal Interface /// - parameter identifier: The identifier of the requested language. diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index 690445a..d048954 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -14,11 +14,13 @@ struct Capture { let name: String - // MARK: - Initializers init?(dictionary: [AnyHashable: Any]) { - guard let name = dictionary["name"] as? String else { return nil } + guard let name = dictionary["name"] as? String else { + return nil + } + self.name = name } } diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index 189fdf8..6aea7f5 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -20,11 +20,12 @@ struct CaptureCollection { return keys } - // MARK: - Initializers init?(dictionary: [AnyHashable: Any]) { - guard let dictionary = dictionary as? [String: [String: String]] else { return nil } + guard let dictionary = dictionary as? [String: [String: String]] else { + return nil + } var captures = [UInt: Capture]() for (key, value) in dictionary { @@ -35,7 +36,6 @@ struct CaptureCollection { self.captures = captures } - // MARK: - Accessing Captures subscript(index: UInt) -> Capture? { diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 4f12f6f..64e1f91 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -23,7 +23,6 @@ public struct Language { static let globalScope = "GLOBAL" - // MARK: - Initializers init?(dictionary: [String: Any], manager: BundleManager) { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index c3010ae..63184ac 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -16,7 +16,6 @@ open class Parser { public typealias Callback = (_ scope: String, _ range: NSRange) -> Void - // MARK: - Properties /// The Language that the parser recognizes @@ -29,14 +28,12 @@ open class Parser { /// Set to true to abort the parsing pass var aborted = false - // MARK: - Initializers public init(language: Language) { self.language = language } - // MARK: - Public open func parse(_ string: String, match callback: Callback) { @@ -47,7 +44,6 @@ open class Parser { parse(match: callback) } - // MARK: - Private /// Parses the string in toParse. Supports incremental parsing. diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index db7dd1c..ffd0193 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -31,7 +31,7 @@ class Pattern: NSObject { var beginCaptures: CaptureCollection? { return _beginCaptures } var end: NSRegularExpression? { return _end } var endCaptures: CaptureCollection? { return _endCaptures } - var applyEndPatternLast: Bool { return _applyEndPatternLast} + var applyEndPatternLast: Bool { return _applyEndPatternLast } var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] @@ -47,7 +47,6 @@ class Pattern: NSObject { fileprivate let debug = true - // MARK: - Initializers init?(dictionary: [AnyHashable: Any], parent: Pattern?, with repository: Repository?, with manager: ReferenceManager) { @@ -104,7 +103,7 @@ class Pattern: NSObject { if self.match == nil && self.begin == nil && self.end == nil && - (dictionary["patterns"] as? [[AnyHashable: Any]] == nil || (dictionary["patterns"] as? [[AnyHashable: Any]])!.count == 0) { + (dictionary["patterns"] as? [[AnyHashable: Any]] == nil || (dictionary["patterns"] as? [[AnyHashable: Any]])!.isEmpty) { print("Attention: pattern not recognized: \(self.name)") return nil } @@ -146,14 +145,13 @@ class Include: Pattern { // MARK: - Properties - var type: ReferenceType {return _type} + var type: ReferenceType { return _type } fileprivate var _type: ReferenceType fileprivate let repositoryRef: String? fileprivate let languageRef: String? fileprivate var associatedRepository: Repository? - // MARK: - Initializers init(reference: String, in repository: Repository? = nil, parent: Pattern?, manager: BundleManager) { @@ -193,7 +191,6 @@ class Include: Pattern { super.init(pattern: include, parent: parent) } - // MARK: - Reference Resolution func resolveInternalReference(with repository: Repository, in language: Language) { @@ -230,7 +227,6 @@ class Include: Pattern { _type = .resolved } - // MARK: - Private fileprivate func replace(with pattern: Pattern) { diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/RefernceManager.swift index 7baed8b..6712e2f 100644 --- a/SyntaxKit/RefernceManager.swift +++ b/SyntaxKit/RefernceManager.swift @@ -21,14 +21,12 @@ class ReferenceManager { fileprivate var includes: [Include] = [] fileprivate weak var bundleManager: BundleManager? - // MARK: - Init init(bundleManager: BundleManager) { self.bundleManager = bundleManager } - // MARK: - Pattern Creation and Resolution func patterns(for patterns: [[AnyHashable: Any]], in repository: Repository?, caller: Pattern?) -> [Pattern] { diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index c651023..d64ff91 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -16,7 +16,6 @@ class Repository { fileprivate var entries: [String: Pattern] = [:] fileprivate weak var parentRepository: Repository? - // MARK: - Initializers init(repo: [String: [AnyHashable: Any]], inParent parent: Repository?, with manager: ReferenceManager) { @@ -33,7 +32,6 @@ class Repository { } } - // MARK: - Accessing Patterns subscript(index: String) -> Pattern? { diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index ebfd2d1..73d3ce5 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -16,7 +16,6 @@ struct Result: Equatable { var range: NSRange let attribute: AnyObject? - // MARK: - Initializers init(identifier: String, range: NSRange, attribute: AnyObject? = nil) { diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index 69d6eac..c2c4f09 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -20,14 +20,12 @@ class ResultSet { fileprivate var _results = [Result]() fileprivate var _range: NSRange - // MARK: - Initializers init(startingRange range: NSRange) { _range = range } - // MARK: - Adding func extend(with range: NSRange) { diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 5c148bb..414e3dc 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -84,14 +84,12 @@ struct ScopedString { return Scope(identifier: "BaseNameString", range: NSRange(location: 0, length: (string as NSString).length), attribute: nil) } - // MARK: - Initializers init(string: String) { self.string = string } - // MARK: - Interface func numberOfScopes() -> Int { @@ -200,7 +198,7 @@ struct ScopedString { levels[level].remove(at: scope) } } - if levels[level].count == 0 { + if levels[level].isEmpty { levels.remove(at: level) } } @@ -242,7 +240,7 @@ struct ScopedString { levels[level][scope].range = theRange } } - if levels[level].count == 0 { + if levels[level].isEmpty { levels.remove(at: level) } } @@ -282,7 +280,6 @@ struct ScopedString { return result } - // MARK: - Private fileprivate func findScopeIntersection(with range: NSRange, at level: [Scope]) -> Scope? { diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 60005a3..03f2bed 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -16,7 +16,6 @@ class AttributedParserTests: XCTestCase { let manager = getBundleManager() var parser: AttributedParser! - // MARK: - Tests override func setUp() { diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 4c7414e..9fa916f 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -20,7 +20,6 @@ class IncrementalParsingTests: XCTestCase { var totalRange: NSRange? var input = "" - // MARK: - Tests override func setUp() { @@ -93,7 +92,6 @@ class IncrementalParsingTests: XCTestCase { } } - // MARK: - Helpers fileprivate func getParsingOperation() -> AttributedParsingOperation { diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index a51e6c7..1169053 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -15,7 +15,6 @@ class LanguageTests: XCTestCase { let manager = getBundleManager() - // MARK: - Tests func testYaml() { diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index a6b6de5..56cba86 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -24,7 +24,6 @@ class ParserTests: XCTestCase { parser = Parser(language: yaml) } - func testParsingBeginEnd() { var stringQuoted: NSRange? var punctuationBegin: NSRange? diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 2bda05b..81a6a29 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -17,7 +17,6 @@ class SwiftBaselineHighlightingTests: XCTestCase { let manager = getBundleManager() var parser: AttributedParser! - // MARK: - Tests override func setUp() { diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 9327427..0d106fa 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -17,7 +17,6 @@ class ThemeTests: XCTestCase { var tomorrow: Theme! var solarized: Theme! - // MARK: - Tests override func setUp() { diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 909b169..84bfa4d 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -33,7 +33,6 @@ public struct Theme { return attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color ?? Color.black } - // MARK: - Initializers init?(dictionary: [String: Any]) { From 6fde1c831cb8c629c0842047ef5912d168904349 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Wed, 9 Nov 2016 16:49:35 +0100 Subject: [PATCH 088/110] naming fixes --- Readme.markdown | 2 +- SyntaxKit/{RefernceManager.swift => ReferenceManager.swift} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename SyntaxKit/{RefernceManager.swift => ReferenceManager.swift} (100%) diff --git a/Readme.markdown b/Readme.markdown index aef0ee4..b0938c2 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -41,7 +41,7 @@ Once you have a BundleManager, you can get started: import SyntaxKit let manager = BundleManager() { identifier, isLanguage in - NSURL(string: "Location of Bundles/" + identifier + ".plist") + URL(string: "Location of Bundles/" + identifier + ".plist") } let yaml = manager.language(withIdentifier: "source.yaml")! diff --git a/SyntaxKit/RefernceManager.swift b/SyntaxKit/ReferenceManager.swift similarity index 100% rename from SyntaxKit/RefernceManager.swift rename to SyntaxKit/ReferenceManager.swift From 9f260676a5e9989698062833c734f06f64967db9 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 11 Nov 2016 20:06:28 +0100 Subject: [PATCH 089/110] add kind TMFileType for TextMate files --- SyntaxKit/BundleManager.swift | 16 ++++++++++------ SyntaxKit/Tests/TestHelper.swift | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 6097490..fee3c4d 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -14,16 +14,20 @@ open class BundleManager { + public enum TextMateFileType { + case Language, Theme + } + // MARK: - Types - /// Given an identifier of a grammar file and the format returns a url to the resource. + /// Given an identifier of a grammar file and the format returns a url to + /// the resource. /// /// - parameter identifier: The identifier of the file. Used to map it to /// the name of the file. - /// - parameter isLanguage: Whether the requested file stores a language - /// (.tmLanguage) + /// - parameter kind: The kind of file requested /// - returns: A URL pointing to the resource, if found - public typealias BundleLocationCallback = (_ identifier: String, _ isLanguage: Bool) -> (URL?) + public typealias BundleLocationCallback = (_ identifier: String, _ kind: TextMateFileType) -> (URL?) // MARK: - Properties @@ -83,7 +87,7 @@ open class BundleManager { return theme } - guard let dictURL = self.bundleCallback(identifier, false), + guard let dictURL = self.bundleCallback(identifier, .Theme), let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], let newTheme = Theme(dictionary: plist) else { return nil @@ -108,7 +112,7 @@ open class BundleManager { if indexOfStoredLanguage != nil { return self.dependencies[indexOfStoredLanguage!] } else { - guard let dictURL = self.bundleCallback(identifier, true), + guard let dictURL = self.bundleCallback(identifier, .Language), let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], let newLanguage = Language(dictionary: plist, manager: self) else { return nil diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 7cde91b..102375d 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -22,9 +22,9 @@ func fixture(_ name: String, _ type: String) -> String { } func getBundleManager() -> BundleManager { - return BundleManager { identifier, isLanguage in - let name = isLanguage ? identifier._split(separator: ".")[1] : identifier - let ext = isLanguage ? ".tmLanguage" : ".tmTheme" + return BundleManager { identifier, kind in + let name = kind == .Language ? identifier._split(separator: ".")[1] : identifier + let ext = kind == .Language ? ".tmLanguage" : ".tmTheme" return Bundle(for: LanguageTests.self).url(forResource: name.capitalized, withExtension: ext) ?? URL(fileURLWithPath: "") } } From da1cd522c3114129ec688197ba486409349a1a34 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 11 Nov 2016 20:08:22 +0100 Subject: [PATCH 090/110] update file name in xcode --- SyntaxKit.xcodeproj/project.pbxproj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index c650ff2..64bbd50 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -86,7 +86,7 @@ 8C2EB35A1D4B5126005ECE2B /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; 8C2EB35B1D4B5126005ECE2B /* BundleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CDD6F1A1C71594B0063915A /* BundleManager.swift */; }; 8C2EB35C1D4B5126005ECE2B /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; - 8C2EB35D1D4B5126005ECE2B /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; + 8C2EB35D1D4B5126005ECE2B /* ReferenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */; }; 8C2EB35E1D4B524F005ECE2B /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; 8C2EB35F1D4B524F005ECE2B /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C2EB3601D4B524F005ECE2B /* ScopedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */; }; @@ -113,9 +113,9 @@ 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */; }; - 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; - 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; - 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */; }; + 8CEEC0DE1C411C8600BF3E85 /* ReferenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */; }; + 8CEEC0DF1C411C8600BF3E85 /* ReferenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */; }; + 8CEEC0E01C411C8600BF3E85 /* ReferenceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */; }; 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E31C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CEEC0E11C411F9700BF3E85 /* Repository.swift */; }; @@ -217,7 +217,7 @@ 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedStringTests.swift; sourceTree = ""; }; 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Swift.tmLanguage; sourceTree = ""; }; 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBaselineHighlightingTests.swift; sourceTree = ""; }; - 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RefernceManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ReferenceManager.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 8CEEC0E11C411F9700BF3E85 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Repository.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; /* End PBXFileReference section */ @@ -361,7 +361,7 @@ 211989961B2EC38B00F0D786 /* Theme.swift */, 8CE64FE21C74B48D0007BA57 /* Language.swift */, 8CEEC0E11C411F9700BF3E85 /* Repository.swift */, - 8CEEC0DD1C411C8600BF3E85 /* RefernceManager.swift */, + 8CEEC0DD1C411C8600BF3E85 /* ReferenceManager.swift */, 211989921B2EC38B00F0D786 /* Pattern.swift */, 2119898E1B2EC38B00F0D786 /* CaptureCollection.swift */, 2119898D1B2EC38B00F0D786 /* Capture.swift */, @@ -678,7 +678,7 @@ 8C2EB35A1D4B5126005ECE2B /* ScopedString.swift in Sources */, 8C2EB35B1D4B5126005ECE2B /* BundleManager.swift in Sources */, 8C2EB35C1D4B5126005ECE2B /* Repository.swift in Sources */, - 8C2EB35D1D4B5126005ECE2B /* RefernceManager.swift in Sources */, + 8C2EB35D1D4B5126005ECE2B /* ReferenceManager.swift in Sources */, 8C2EB3581D4B510F005ECE2B /* Language.swift in Sources */, 8C2EB3571D4B50EE005ECE2B /* Color.swift in Sources */, 211826F91D257A7E003F2BF2 /* Theme.swift in Sources */, @@ -716,7 +716,7 @@ 8C08C3C71C36FD6D00D8548F /* Color.swift in Sources */, 8C9003341C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */, 211989C11B2EC40500F0D786 /* CaptureCollection.swift in Sources */, - 8CEEC0DF1C411C8600BF3E85 /* RefernceManager.swift in Sources */, + 8CEEC0DF1C411C8600BF3E85 /* ReferenceManager.swift in Sources */, 211989C71B2EC40500F0D786 /* ResultSet.swift in Sources */, 8C10B6451CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, 8CDD6F1C1C71594B0063915A /* BundleManager.swift in Sources */, @@ -753,7 +753,7 @@ 2119899C1B2EC38B00F0D786 /* Parser.swift in Sources */, 211989991B2EC38B00F0D786 /* CaptureCollection.swift in Sources */, 8C10B6441CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, - 8CEEC0DE1C411C8600BF3E85 /* RefernceManager.swift in Sources */, + 8CEEC0DE1C411C8600BF3E85 /* ReferenceManager.swift in Sources */, 8CEEC0E21C411F9700BF3E85 /* Repository.swift in Sources */, 2119899F1B2EC38B00F0D786 /* ResultSet.swift in Sources */, 8C10B6471CC3937F00740E00 /* Color.swift in Sources */, @@ -790,7 +790,7 @@ 2198CED71B36D5DE00BD463F /* Parser.swift in Sources */, 8CE64FE51C74B48D0007BA57 /* Language.swift in Sources */, 2198CED41B36D5DE00BD463F /* CaptureCollection.swift in Sources */, - 8CEEC0E01C411C8600BF3E85 /* RefernceManager.swift in Sources */, + 8CEEC0E01C411C8600BF3E85 /* ReferenceManager.swift in Sources */, 8C10B6461CC38E5200740E00 /* AttributedParsingOperation.swift in Sources */, 8CEEC0E41C411F9700BF3E85 /* Repository.swift in Sources */, 2198CEDA1B36D5DE00BD463F /* ResultSet.swift in Sources */, From 947940a108849754d6e9dd3e5edf6ebde0c27c2e Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 11 Nov 2016 22:52:05 +0100 Subject: [PATCH 091/110] update readme --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index b0938c2..cc6f64d 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -40,7 +40,7 @@ Once you have a BundleManager, you can get started: ```swift import SyntaxKit -let manager = BundleManager() { identifier, isLanguage in +let manager = BundleManager() { identifier, kind in URL(string: "Location of Bundles/" + identifier + ".plist") } let yaml = manager.language(withIdentifier: "source.yaml")! From 06eeabbb9b48a9806d005583f75eec198e076811 Mon Sep 17 00:00:00 2001 From: Colas Date: Thu, 22 Dec 2016 18:41:47 +0100 Subject: [PATCH 092/110] Module Name cf. http://stackoverflow.com/a/41288614/1670830 --- SyntaxKit.podspec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SyntaxKit.podspec b/SyntaxKit.podspec index 3e9b0b7..9dcc333 100644 --- a/SyntaxKit.podspec +++ b/SyntaxKit.podspec @@ -14,4 +14,6 @@ Pod::Spec.new do |spec| spec.osx.frameworks = 'Foundation', 'AppKit' spec.source_files = 'SyntaxKit/*.{h,m,swift}' + + spec.module_name = 'SyntaxKit' end From f47812b52f9020e287972c37ec7691fb77848b9e Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 27 Dec 2016 20:56:13 +0100 Subject: [PATCH 093/110] fix linter warnings --- SyntaxKit/ScopedString.swift | 10 +++++----- SyntaxKit/Tests/IncrementalParsingTests.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 414e3dc..3a703f1 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -192,7 +192,7 @@ struct ScopedString { assert(NSIntersectionRange(range, baseScope.range).length == range.length) for level in stride(from: (levels.count - 1), through: 0, by: -1) { - for scope in stride(from: (levels[level].count-1), through: 0, by: -1) { + for scope in stride(from: (levels[level].count - 1), through: 0, by: -1) { let theScope = levels[level][scope] if range.entirelyContains(theScope.range) { levels[level].remove(at: scope) @@ -231,7 +231,7 @@ struct ScopedString { mutableString?.deleteCharacters(in: range) self.string = mutableString?.copy() as? String ?? "" for level in stride(from: (levels.count - 1), through: 0, by: -1) { - for scope in stride(from: (levels[level].count-1), through: 0, by: -1) { + for scope in stride(from: (levels[level].count - 1), through: 0, by: -1) { var theRange = levels[level][scope].range theRange.removeIndexes(from: range) if theRange.isEmpty() { @@ -271,10 +271,10 @@ struct ScopedString { result += levelString + "\n" } var numberString = "" - for i in 0...(string as NSString).length/10 { - let numDigits = ("\(i*10)" as NSString).length + for i in 0...(string as NSString).length / 10 { + let numDigits = ("\(i * 10)" as NSString).length let dashes = String(repeating: "-", count: 9 - numDigits) - numberString += "\(i*10)\(dashes)|" + numberString += "\(i * 10)\(dashes)|" } result += numberString + "\n" return result diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 9fa916f..81ef3be 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -95,7 +95,7 @@ class IncrementalParsingTests: XCTestCase { // MARK: - Helpers fileprivate func getParsingOperation() -> AttributedParsingOperation { - return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], sender: AttributedParsingOperation) in + return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], _: AttributedParsingOperation) in for result in results { if self.totalRange == nil { self.totalRange = result.range From 020995f66f01f0ccbe979ca4762952b5c70948d8 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Thu, 5 Jan 2017 22:50:52 +0100 Subject: [PATCH 094/110] add performance test to measure #20 --- SyntaxKit.xcodeproj/project.pbxproj | 32 + SyntaxKit/Tests/PerformanceTests.swift | 43 + .../Tests/Resources/Fixtures/Latex.tmLanguage | 1529 +++++++++++++++++ .../Tests/Resources/Fixtures/Tex.tmLanguage | 424 +++++ .../Tests/Resources/Fixtures/textest.tex.txt | 644 +++++++ 5 files changed, 2672 insertions(+) create mode 100644 SyntaxKit/Tests/PerformanceTests.swift create mode 100644 SyntaxKit/Tests/Resources/Fixtures/Latex.tmLanguage create mode 100644 SyntaxKit/Tests/Resources/Fixtures/Tex.tmLanguage create mode 100644 SyntaxKit/Tests/Resources/Fixtures/textest.tex.txt diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 64bbd50..2f2d136 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -93,6 +93,18 @@ 8C2EB3611D4B525A005ECE2B /* Swift.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */; }; 8C2EB3621D4B525A005ECE2B /* swifttest.swift.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */; }; 8C2EB3631D4B525A005ECE2B /* Solarized.tmTheme in Resources */ = {isa = PBXBuildFile; fileRef = 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */; }; + 8C4D09C01E1EAF750034974A /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4D09BF1E1EAF750034974A /* PerformanceTests.swift */; }; + 8C4D09C11E1EAF750034974A /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4D09BF1E1EAF750034974A /* PerformanceTests.swift */; }; + 8C4D09C21E1EAF750034974A /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4D09BF1E1EAF750034974A /* PerformanceTests.swift */; }; + 8C4D09C41E1EB0670034974A /* Latex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C31E1EB0670034974A /* Latex.tmLanguage */; }; + 8C4D09C51E1EB0670034974A /* Latex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C31E1EB0670034974A /* Latex.tmLanguage */; }; + 8C4D09C61E1EB0670034974A /* Latex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C31E1EB0670034974A /* Latex.tmLanguage */; }; + 8C4D09C81E1EB07D0034974A /* Tex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C71E1EB07D0034974A /* Tex.tmLanguage */; }; + 8C4D09C91E1EB07D0034974A /* Tex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C71E1EB07D0034974A /* Tex.tmLanguage */; }; + 8C4D09CA1E1EB07D0034974A /* Tex.tmLanguage in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09C71E1EB07D0034974A /* Tex.tmLanguage */; }; + 8C4D09CC1E1EB16C0034974A /* textest.tex.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09CB1E1EB16C0034974A /* textest.tex.txt */; }; + 8C4D09CD1E1EB16C0034974A /* textest.tex.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09CB1E1EB16C0034974A /* textest.tex.txt */; }; + 8C4D09CE1E1EB16C0034974A /* textest.tex.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8C4D09CB1E1EB16C0034974A /* textest.tex.txt */; }; 8C71A05D1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C71A05E1C59587700041EC6 /* IncrementalParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */; }; 8C9003331C5C0B0D00CBA5B0 /* ScopedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */; }; @@ -208,6 +220,10 @@ 2198CECA1B36D5D700BD463F /* SyntaxKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyntaxKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C08C3C61C36FD6D00D8548F /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Color.swift; path = ../Color.swift; sourceTree = ""; }; 8C10B6431CC38E5200740E00 /* AttributedParsingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AttributedParsingOperation.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 8C4D09BF1E1EAF750034974A /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = ""; }; + 8C4D09C31E1EB0670034974A /* Latex.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Latex.tmLanguage; sourceTree = ""; }; + 8C4D09C71E1EB07D0034974A /* Tex.tmLanguage */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Tex.tmLanguage; sourceTree = ""; }; + 8C4D09CB1E1EB16C0034974A /* textest.tex.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = textest.tex.txt; sourceTree = ""; }; 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncrementalParsingTests.swift; sourceTree = ""; }; 8C9003321C5C0B0D00CBA5B0 /* ScopedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScopedString.swift; sourceTree = ""; }; 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = swifttest.swift.txt; sourceTree = ""; }; @@ -293,6 +309,7 @@ 211989821B2EB18000F0D786 /* TestHelper.swift */, 211989881B2EB8D400F0D786 /* ParserTests.swift */, 210299D01B2E8924009C61EE /* AttributedParserTests.swift */, + 8C4D09BF1E1EAF750034974A /* PerformanceTests.swift */, 8CE8D1161C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift */, 8C71A05C1C59587700041EC6 /* IncrementalParsingTests.swift */, 8CE6BE2D1C5D1BBA002676BD /* ScopedStringTests.swift */, @@ -316,7 +333,10 @@ children = ( 210BF26F1B37C0A2008AA4F0 /* Ruby.tmLanguage */, 8CE8D1131C4EBF58005A86B3 /* Swift.tmLanguage */, + 8C4D09C31E1EB0670034974A /* Latex.tmLanguage */, + 8C4D09C71E1EB07D0034974A /* Tex.tmLanguage */, 8CB2FD221C4D877D008ECD6D /* swifttest.swift.txt */, + 8C4D09CB1E1EB16C0034974A /* textest.tex.txt */, 210BF26C1B37C04E008AA4F0 /* test.rb.txt */, 8CB2FD251C4D87D6008ECD6D /* Solarized.tmTheme */, 2119897E1B2EAF0900F0D786 /* Tomorrow.tmTheme */, @@ -611,10 +631,13 @@ 8C2EB3611D4B525A005ECE2B /* Swift.tmLanguage in Resources */, 8C2EB3621D4B525A005ECE2B /* swifttest.swift.txt in Resources */, 8C2EB3631D4B525A005ECE2B /* Solarized.tmTheme in Resources */, + 8C4D09CA1E1EB07D0034974A /* Tex.tmLanguage in Resources */, 211826FF1D257A8A003F2BF2 /* Ruby.tmLanguage in Resources */, + 8C4D09C61E1EB0670034974A /* Latex.tmLanguage in Resources */, 211827021D257A8A003F2BF2 /* Yaml.tmLanguage in Resources */, 211827001D257A8A003F2BF2 /* test.rb.txt in Resources */, 211827011D257A8A003F2BF2 /* Tomorrow.tmTheme in Resources */, + 8C4D09CE1E1EB16C0034974A /* textest.tex.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -632,10 +655,13 @@ 8CE8D1151C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 210BF2711B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */, 211989CA1B2EC40900F0D786 /* Yaml.tmLanguage in Resources */, + 8C4D09C91E1EB07D0034974A /* Tex.tmLanguage in Resources */, 211989C91B2EC40900F0D786 /* Tomorrow.tmTheme in Resources */, + 8C4D09C51E1EB0670034974A /* Latex.tmLanguage in Resources */, 8CB2FD271C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, 210BF26E1B37C04E008AA4F0 /* test.rb.txt in Resources */, 8CB2FD281C4D891A008ECD6D /* swifttest.swift.txt in Resources */, + 8C4D09CD1E1EB16C0034974A /* textest.tex.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -653,10 +679,13 @@ 8CE8D1141C4EBF58005A86B3 /* Swift.tmLanguage in Resources */, 210BF2701B37C0A2008AA4F0 /* Ruby.tmLanguage in Resources */, 211989811B2EAF0900F0D786 /* Yaml.tmLanguage in Resources */, + 8C4D09C81E1EB07D0034974A /* Tex.tmLanguage in Resources */, 211989801B2EAF0900F0D786 /* Tomorrow.tmTheme in Resources */, + 8C4D09C41E1EB0670034974A /* Latex.tmLanguage in Resources */, 8CB2FD261C4D87D6008ECD6D /* Solarized.tmTheme in Resources */, 210BF26D1B37C04E008AA4F0 /* test.rb.txt in Resources */, 8CAEC6BA1D3BB297001C57D3 /* swifttest.swift.txt in Resources */, + 8C4D09CC1E1EB16C0034974A /* textest.tex.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -704,6 +733,7 @@ 211826FC1D257A84003F2BF2 /* AttributedParserTests.swift in Sources */, 211826FB1D257A84003F2BF2 /* ParserTests.swift in Sources */, 211826FE1D257A84003F2BF2 /* ThemeTests.swift in Sources */, + 8C4D09C21E1EAF750034974A /* PerformanceTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -741,6 +771,7 @@ 211989CB1B2EC40C00F0D786 /* TestHelper.swift in Sources */, 8CE8D1181C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, 211989CD1B2EC40C00F0D786 /* AttributedParserTests.swift in Sources */, + 8C4D09C11E1EAF750034974A /* PerformanceTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -778,6 +809,7 @@ 210299DE1B2E892E009C61EE /* LanguageTests.swift in Sources */, 8CE8D1171C4EBF8F005A86B3 /* SwiftBaselineHighlightingTests.swift in Sources */, 2119898B1B2EBA2C00F0D786 /* ThemeTests.swift in Sources */, + 8C4D09C01E1EAF750034974A /* PerformanceTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SyntaxKit/Tests/PerformanceTests.swift b/SyntaxKit/Tests/PerformanceTests.swift new file mode 100644 index 0000000..4a94929 --- /dev/null +++ b/SyntaxKit/Tests/PerformanceTests.swift @@ -0,0 +1,43 @@ +// +// PerformanceTests.swift +// SyntaxKit +// +// Created by Alexander Hedges on 05.01.17. +// Copyright © 2017 Sam Soffes. All rights reserved. +// + +import XCTest +import SyntaxKit + +class PerformanceTests: XCTestCase { + + // MARK: - Properties + + let manager = getBundleManager() + var parser: AttributedParser! + + override func setUp() { + super.setUp() + let latex = manager.language(withIdentifier: "source.Latex")! + let solarized = manager.theme(withIdentifier: "Solarized")! + parser = AttributedParser(language: latex, theme: solarized) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLongTexFilePerformance() { + let input = fixture("textest.tex", "txt") + self.measure { + _ = self.parser.attributedString(for: input) + } + } + +} diff --git a/SyntaxKit/Tests/Resources/Fixtures/Latex.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Latex.tmLanguage new file mode 100644 index 0000000..dafc016 --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/Latex.tmLanguage @@ -0,0 +1,1529 @@ + + + + + fileTypes + + tex + + firstLineMatch + ^\\documentclass(?!.*\{beamer\}) + keyEquivalent + ^~L + name + LaTeX + patterns + + + comment + This scope identifies partially typed commands such as `\tab`. We use this to trigger “Command Completion” only when it makes sense. + match + (?<=\\[\w@]|\\[\w@]{2}|\\[\w@]{3}|\\[\w@]{4}|\\[\w@]{5}|\\[\w@]{6})\s + name + meta.space-after-command.latex + + + begin + ((\\)(?:usepackage|documentclass))(?:(\[)([^\]]*)(\]))?(\{) + beginCaptures + + 1 + + name + keyword.control.preamble.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + 6 + + name + punctuation.definition.arguments.begin.latex + + + contentName + support.class.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.end.latex + + + name + meta.preamble.latex + patterns + + + include + $self + + + + + begin + ((\\)(?:include|input))(\{) + beginCaptures + + 1 + + name + keyword.control.include.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + + contentName + support.class.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.end.latex + + + name + meta.include.latex + patterns + + + include + $self + + + + + begin + (?x) + ( # Capture 1 + (\\) # Marker + ( + (?:sub){0,2}section # Functions + | (?:sub)?paragraph + | chapter|part|addpart + | addchap|addsec|minisec + ) + (?:\*)? # Optional Unnumbered + ) + (?: + (\[)([^\[]*?)(\]) # Optional Title + )?? + (\{) # Opening Bracket + + beginCaptures + + 1 + + name + support.function.section.latex + + 2 + + name + punctuation.definition.function.latex + + 4 + + name + punctuation.definition.arguments.optional.begin.latex + + 5 + + name + entity.name.section.latex + + 6 + + name + punctuation.definition.arguments.optional.end.latex + + 7 + + name + punctuation.definition.arguments.begin.latex + + + comment + this works OK with all kinds of crazy stuff as long as section is one line + contentName + entity.name.section.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.end.latex + + + name + meta.function.section.$3.latex + patterns + + + include + $self + + + + + begin + (^\s*)?(?=\\begin\{lstlisting\}) + beginCaptures + + 0 + + name + punctuation.whitespace.embedded.leading.latex + + + end + (?!\G)(\s*$\n?)? + endCaptures + + 0 + + name + punctuation.whitespace.embedded.trailing.latex + + + patterns + + + begin + ((\\)begin)(\{)(lstlisting)(\})(?:(\[).*(\]))?(\s*%.*\n?)? + captures + + 1 + + name + meta.function.embedded.latex + + 2 + + name + support.function.be.latex + + 3 + + name + punctuation.definition.function.latex + + 4 + + name + punctuation.definition.arguments.begin.latex + + 5 + + name + variable.parameter.function.latex + + 6 + + name + punctuation.definition.arguments.end.latex + + 7 + + name + punctuation.definition.arguments.optional.begin.latex + + 8 + + name + punctuation.definition.arguments.optional.end.latex + + 9 + + name + comment.line.percentage.latex + + + contentName + meta.function.embedded.latex + end + (((\\)end)(\{)(lstlisting)(\})) + name + meta.embedded.block.generic + + + + + begin + (?:\s*)((\\)begin)(\{)((?:V|v)erbatim|alltt)(\}) + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + contentName + markup.raw.verbatim.latex + end + ((\\)end)(\{)(\4)(\}) + name + meta.function.verbatim.latex + + + captures + + 1 + + name + support.function.url.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + markup.underline.link.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + match + (?:\s*)((\\)(?:url|href))(\{)([^}]*)(\}) + name + meta.function.link.url.latex + + + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + comment + These two patterns match the \begin{document} and \end{document} commands, so that the environment matching pattern following them will ignore those commands. + match + (?:\s*)((\\)begin)(\{)(document)(\}) + name + meta.function.begin-document.latex + + + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + match + (?:\s*)((\\)end)(\{)(document)(\}) + name + meta.function.end-document.latex + + + begin + (?x) + (?:\s*) # Optional whitespace + ((\\)begin) # Marker - Function + (\{) # Open Bracket + ( + (?: + align|equation|eqnarray # Argument + | multline|aligned|alignat + | split|gather|gathered + ) + (?:\*)? # Optional Unnumbered + ) + (\}) # Close Bracket + (\s*\n)? # Match to end of line absent of content + + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + contentName + string.other.math.block.environment.latex + end + (?x) + (?:\s*) # Optional whitespace + ((\\)end) # Marker - Function + (\{) # Open Bracket + (\4) # Previous capture from begin + (\}) # Close Bracket + (?:\s*\n)? # Match to end of line absent of content + + name + meta.function.environment.math.latex + patterns + + + include + $base + + + + + begin + (?x) + (?:\s*) # Optional whitespace + ((\\)begin) # Marker - Function + (\{) # Open Bracket + (array|tabular[xy*]?) + (\}) # Close Bracket + (\s*\n)? # Match to end of line absent of content + + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.end.latex + + + contentName + meta.data.environment.tabular.latex + end + (?x) + (?:\s*) # Optional whitespace + ((\\)end) # Marker - Function + (\{) # Open Bracket + (\4) # Previous capture from begin + (\}) # Close Bracket + (?:\s*\n)? # Match to end of line absent of content + + name + meta.function.environment.tabular.latex + patterns + + + match + \\\\ + name + punctuation.definition.table.row.latex + + + begin + (?:^|(?<=\\\\))(?!\\\\|\s*\\end\{(?:tabular|array)) + end + (?=\\\\|\s*\\end\{(?:tabular|array)) + name + meta.row.environment.tabular.latex + patterns + + + match + & + name + punctuation.definition.table.cell.latex + + + begin + (?:^|(?<=&))((?!&|\\\\|$)) + end + (?=&|\\\\|\s*\\end\{(?:tabular|array)) + name + meta.cell.environment.tabular.latex + patterns + + + include + $base + + + + + include + $base + + + + + include + $base + + + + + begin + (?:\s*)((\\)begin)(\{)(itemize|enumerate|description|list)(\}) + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.latex + + + end + ((\\)end)(\{)(\4)(\})(?:\s*\n)? + name + meta.function.environment.list.latex + patterns + + + include + $base + + + + + begin + (?:\s*)((\\)begin)(\{)(tikzpicture)(\}) + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.latex + + + end + ((\\)end)(\{)(tikzpicture)(\})(?:\s*\n)? + name + meta.function.environment.latex.tikz + patterns + + + include + $self + + + + + begin + (?:\s*)((\\)begin)(\{)(\w+[*]?)(\}) + captures + + 1 + + name + support.function.be.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.arguments.latex + + 4 + + name + variable.parameter.function.latex + + 5 + + name + punctuation.definition.arguments.latex + + + end + ((\\)end)(\{)(\4)(\})(?:\s*\n)? + name + meta.function.environment.general.latex + patterns + + + include + $base + + + + + captures + + 1 + + name + punctuation.definition.function.latex + + + match + (\\)(newcommand|renewcommand)\b + name + storage.type.function.latex + + + begin + ((\\)marginpar)(\{) + beginCaptures + + 1 + + name + support.function.marginpar.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.marginpar.begin.latex + + + contentName + meta.paragraph.margin.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.marginpar.end.latex + + + patterns + + + include + $base + + + + + begin + ((\\)footnote)((?:\[[^\[]*?\])*)(\{) + beginCaptures + + 1 + + name + support.function.footnote.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + patterns + + + captures + + 1 + + name + punctuation.definition.arguments.begin.latex + + 2 + + name + variable.parameter.latex + + 3 + + name + punctuation.definition.arguments.end.latex + + + match + (\[)([^\[]*?)(\]) + + + + 4 + + name + punctuation.definition.footnote.begin.latex + + + contentName + meta.footnote.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.footnote.end.latex + + + patterns + + + include + $base + + + + + begin + ((\\)emph)(\{) + beginCaptures + + 1 + + name + support.function.emph.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.emph.begin.latex + + + contentName + markup.italic.emph.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.emph.end.latex + + + name + meta.function.emph.latex + patterns + + + include + $base + + + + + begin + ((\\)textit)(\{) + captures + + 1 + + name + support.function.textit.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.textit.begin.latex + + + comment + We put the keyword in a capture and name this capture, so that disabling spell checking for “keyword” won't be inherited by the argument to \textit{...}. + +Put specific matches for particular LaTeX keyword.functions before the last two more general functions + contentName + markup.italic.textit.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.textit.end.latex + + + name + meta.function.textit.latex + patterns + + + include + $base + + + + + begin + ((\\)textbf)(\{) + captures + + 1 + + name + support.function.textbf.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.textbf.begin.latex + + + contentName + markup.bold.textbf.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.textbf.end.latex + + + name + meta.function.textbf.latex + patterns + + + include + $base + + + + + begin + ((\\)texttt)(\{) + captures + + 1 + + name + support.function.texttt.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.texttt.begin.latex + + + contentName + markup.raw.texttt.latex + end + \} + endCaptures + + 0 + + name + punctuation.definition.texttt.end.latex + + + name + meta.function.texttt.latex + patterns + + + include + $base + + + + + captures + + 0 + + name + keyword.other.item.latex + + 1 + + name + punctuation.definition.keyword.latex + + + match + (\\)item\b + name + meta.scope.item.latex + + + begin + (?x) + ( + (\\) # Marker + (?:auto)?(?:foot)?(?:full)?(?:no)?(?:short)? # Function Name + [cC]ite + (?:al)?(?:t|p|author|year(?:par)?|title)?[ANP]* + \*? # Optional Unabreviated + ) + (?:(\[)[^\]]*(\]))? # Optional + (?:(\[)[^\]]*(\]))? # Arguments + (\{) # Opening Bracket + + captures + + 1 + + name + keyword.control.cite.latex + + 2 + + name + punctuation.definition.keyword.latex + + 3 + + name + punctuation.definition.arguments.optional.begin.latex + + 4 + + name + punctuation.definition.arguments.optional.end.latex + + 5 + + name + punctuation.definition.arguments.optional.begin.latex + + 6 + + name + punctuation.definition.arguments.optional.end.latex + + 7 + + name + punctuation.definition.arguments.latex + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.latex + + + name + meta.citation.latex + patterns + + + match + [\w:.]+ + name + constant.other.reference.citation.latex + + + + + begin + ((\\)(?:\w*[r|R]ef\*?))(\{) + beginCaptures + + 1 + + name + keyword.control.ref.latex + + 2 + + name + punctuation.definition.keyword.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.begin.latex + + + name + meta.reference.label.latex + patterns + + + match + [a-zA-Z0-9\.,:/*!^_-] + name + constant.other.reference.label.latex + + + + + begin + ((\\)label)(\{) + beginCaptures + + 1 + + name + keyword.control.label.latex + + 2 + + name + punctuation.definition.keyword.latex + + 3 + + name + punctuation.definition.arguments.begin.latex + + + end + \} + endCaptures + + 0 + + name + punctuation.definition.arguments.end.latex + + + name + meta.definition.label.latex + patterns + + + match + [a-zA-Z0-9\.,:/*!^_-] + name + variable.parameter.definition.label.latex + + + + + begin + ((\\)(?:verb|lstinline)[\*]?)\s*((\\)scantokens)(\{) + beginCaptures + + 1 + + name + support.function.verb.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + support.function.verb.latex + + 4 + + name + punctuation.definition.verb.latex + + 5 + + name + punctuation.definition.begin.latex + + + contentName + markup.raw.verb.latex + end + (\}) + endCaptures + + 1 + + name + punctuation.definition.end.latex + + + name + meta.function.verb.latex + patterns + + + include + $self + + + + + captures + + 1 + + name + support.function.verb.latex + + 2 + + name + punctuation.definition.function.latex + + 3 + + name + punctuation.definition.verb.latex + + 4 + + name + markup.raw.verb.latex + + 5 + + name + punctuation.definition.verb.latex + + + match + ((\\)(?:verb|lstinline)[\*]?)\s*((?<=\s)\S|[^a-zA-Z])(.*?)(\3|$) + name + meta.function.verb.latex + + + begin + "` + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + "' + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.quoted.double.european.latex + patterns + + + include + $base + + + + + begin + `` + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + ''|" + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.quoted.double.latex + patterns + + + include + $base + + + + + begin + "> + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + "< + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.quoted.double.guillemot.latex + patterns + + + include + $base + + + + + begin + "< + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + "> + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.quoted.double.guillemot.latex + patterns + + + include + $base + + + + + begin + \\\( + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + \\\) + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.other.math.latex + patterns + + + include + $base + + + + + begin + \\\[ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.latex + + + end + \\\] + endCaptures + + 0 + + name + punctuation.definition.string.end.latex + + + name + string.other.math.latex + patterns + + + include + $base + + + + + match + (?<!\S)'.*?' + name + invalid.illegal.string.quoted.single.latex + + + match + (?<!\S)".*?" + name + invalid.illegal.string.quoted.double.latex + + + captures + + 1 + + name + punctuation.definition.constant.latex + + + match + (\\)(text(s(terling|ixoldstyle|urd|e(ction|venoldstyle|rvicemark))|yen|n(ineoldstyle|umero|aira)|c(ircledP|o(py(left|right)|lonmonetary)|urrency|e(nt(oldstyle)?|lsius))|t(hree(superior|oldstyle|quarters(emdash)?)|i(ldelow|mes)|w(o(superior|oldstyle)|elveudash)|rademark)|interrobang(down)?|zerooldstyle|o(hm|ne(superior|half|oldstyle|quarter)|penbullet|rd(feminine|masculine))|d(i(scount|ed|v(orced)?)|o(ng|wnarrow|llar(oldstyle)?)|egree|agger(dbl)?|blhyphen(char)?)|uparrow|p(ilcrow|e(so|r(t(housand|enthousand)|iodcentered))|aragraph|m)|e(stimated|ightoldstyle|uro)|quotes(traight(dblbase|base)|ingle)|f(iveoldstyle|ouroldstyle|lorin|ractionsolidus)|won|l(not|ira|e(ftarrow|af)|quill|angle|brackdbl)|a(s(cii(caron|dieresis|acute|grave|macron|breve)|teriskcentered)|cutedbl)|r(ightarrow|e(cipe|ferencemark|gistered)|quill|angle|brackdbl)|g(uarani|ravedbl)|m(ho|inus|u(sicalnote)?|arried)|b(igcircle|orn|ullet|lank|a(ht|rdbl)|rokenbar)))\b + name + constant.character.latex + + + captures + + 1 + + name + punctuation.definition.column-specials.begin.latex + + 2 + + name + punctuation.definition.column-specials.end.latex + + + match + (?:<|>)(\{)\$(\}) + name + meta.column-specials.latex + + + include + text.tex + + + scopeName + text.tex.latex + uuid + 3BEEA00C-6B1D-11D9-B8AD-000D93589AF6 + + diff --git a/SyntaxKit/Tests/Resources/Fixtures/Tex.tmLanguage b/SyntaxKit/Tests/Resources/Fixtures/Tex.tmLanguage new file mode 100644 index 0000000..403c8a6 --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/Tex.tmLanguage @@ -0,0 +1,424 @@ + + + + + fileTypes + + sty + cls + bbx + cbx + + name + TeX + patterns + + + captures + + 1 + + name + punctuation.definition.keyword.tex + + + match + (\\)(backmatter|else|fi|frontmatter|ftrue|mainmatter|if(case|cat|dim|eof|false|hbox|hmode|inner|mmode|num|odd|undefined|vbox|vmode|void|x)?)\b + name + keyword.control.tex + + + captures + + 1 + + name + keyword.control.catcode.tex + + 2 + + name + punctuation.definition.keyword.tex + + 3 + + name + punctuation.separator.key-value.tex + + 4 + + name + constant.numeric.category.tex + + + match + ((\\)catcode)`(?:\\)?.(=)(\d+) + name + meta.catcode.tex + + + begin + (^[ \t]+)?(?=%) + beginCaptures + + 1 + + name + punctuation.whitespace.comment.leading.tex + + + end + $\n? + patterns + + + begin + %: + beginCaptures + + 0 + + name + punctuation.definition.comment.tex + + + end + (?=$\n?) + name + comment.line.percentage.semicolon.texshop.tex + + + begin + ^(%!TEX) (\S*) = + beginCaptures + + 1 + + name + punctuation.definition.comment.tex + + + end + (?=$\n?) + name + comment.line.percentage.directive.texshop.tex + + + begin + % + beginCaptures + + 0 + + name + punctuation.definition.comment.tex + + + end + (?=$\n?) + name + comment.line.percentage.tex + + + + + begin + \{ + beginCaptures + + 0 + + name + punctuation.section.group.begin.tex + + + end + \} + endCaptures + + 0 + + name + punctuation.section.group.end.tex + + + name + meta.group.braces.tex + patterns + + + include + $base + + + + + match + [\[\]] + name + punctuation.definition.brackets.tex + + + begin + \$\$ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.tex + + + end + \$\$ + endCaptures + + 0 + + name + punctuation.definition.string.end.tex + + + name + string.other.math.block.tex + patterns + + + include + #math + + + include + $self + + + + + match + \\\\ + name + constant.character.newline.tex + + + begin + \$ + beginCaptures + + 0 + + name + punctuation.definition.string.begin.tex + + + end + \$ + endCaptures + + 0 + + name + punctuation.definition.string.end.tex + + + name + string.other.math.tex + patterns + + + match + \\\$ + name + constant.character.escape.tex + + + include + #math + + + include + $self + + + + + captures + + 1 + + name + punctuation.definition.function.tex + + + match + (\\)[A-Za-z@]+ + name + support.function.general.tex + + + captures + + 1 + + name + punctuation.definition.keyword.tex + + + match + (\\)[^a-zA-Z@] + name + constant.character.escape.tex + + + match + «press a-z and space for greek letter»[a-zA-Z]* + name + meta.placeholder.greek.tex + + + repository + + math + + patterns + + + captures + + 1 + + name + punctuation.definition.constant.math.tex + + + match + (\\)(s(s(earrow|warrow|lash)|h(ort(downarrow|uparrow|parallel|leftarrow|rightarrow|mid)|arp)|tar|i(gma|m(eq)?)|u(cc(sim|n(sim|approx)|curlyeq|eq|approx)?|pset(neq(q)?|plus(eq)?|eq(q)?)?|rd|m|bset(neq(q)?|plus(eq)?|eq(q)?)?)|p(hericalangle|adesuit)|e(tminus|arrow)|q(su(pset(eq)?|bset(eq)?)|c(up|ap)|uare)|warrow|m(ile|all(s(etminus|mile)|frown)))|h(slash|ook(leftarrow|rightarrow)|eartsuit|bar)|R(sh|ightarrow|e|bag)|Gam(e|ma)|n(s(hort(parallel|mid)|im|u(cc(eq)?|pseteq(q)?|bseteq))|Rightarrow|n(earrow|warrow)|cong|triangle(left(eq(slant)?)?|right(eq(slant)?)?)|i(plus)?|u|p(lus|arallel|rec(eq)?)|e(q|arrow|g|xists)|v(dash|Dash)|warrow|le(ss|q(slant|q)?|ft(arrow|rightarrow))|a(tural|bla)|VDash|rightarrow|g(tr|eq(slant|q)?)|mid|Left(arrow|rightarrow))|c(hi|irc(eq|le(d(circ|S|dash|ast)|arrow(left|right)))?|o(ng|prod|lon|mplement)|dot(s|p)?|u(p|r(vearrow(left|right)|ly(eq(succ|prec)|vee(downarrow|uparrow)?|wedge(downarrow|uparrow)?)))|enterdot|lubsuit|ap)|Xi|Maps(to(char)?|from(char)?)|B(ox|umpeq|bbk)|t(h(ick(sim|approx)|e(ta|refore))|imes|op|wohead(leftarrow|rightarrow)|a(u|lloblong)|riangle(down|q|left(eq(slant)?)?|right(eq(slant)?)?)?)|i(n(t(er(cal|leave))?|plus|fty)?|ota|math)|S(igma|u(pset|bset))|zeta|o(slash|times|int|dot|plus|vee|wedge|lessthan|greaterthan|m(inus|ega)|b(slash|long|ar))|d(i(v(ideontimes)?|a(g(down|up)|mond(suit)?)|gamma)|o(t(plus|eq(dot)?)|ublebarwedge|wn(harpoon(left|right)|downarrows|arrow))|d(ots|agger)|elta|a(sh(v|leftarrow|rightarrow)|leth|gger))|Y(down|up|left|right)|C(up|ap)|u(n(lhd|rhd)|p(silon|harpoon(left|right)|downarrow|uparrows|lus|arrow)|lcorner|rcorner)|jmath|Theta|Im|p(si|hi|i(tchfork)?|erp|ar(tial|allel)|r(ime|o(d|pto)|ec(sim|n(sim|approx)|curlyeq|eq|approx)?)|m)|e(t(h|a)|psilon|q(slant(less|gtr)|circ|uiv)|ll|xists|mptyset)|Omega|D(iamond|ownarrow|elta)|v(d(ots|ash)|ee(bar)?|Dash|ar(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|curly(vee|wedge)|t(heta|imes|riangle(left|right)?)|o(slash|circle|times|dot|plus|vee|wedge|lessthan|ast|greaterthan|minus|b(slash|ar))|p(hi|i|ropto)|epsilon|kappa|rho|bigcirc))|kappa|Up(silon|downarrow|arrow)|Join|f(orall|lat|a(t(s(emi|lash)|bslash)|llingdotseq)|rown)|P(si|hi|i)|w(p|edge|r)|l(hd|n(sim|eq(q)?|approx)|ceil|times|ightning|o(ng(left(arrow|rightarrow)|rightarrow|maps(to|from))|zenge|oparrow(left|right))|dot(s|p)|e(ss(sim|dot|eq(qgtr|gtr)|approx|gtr)|q(slant|q)?|ft(slice|harpoon(down|up)|threetimes|leftarrows|arrow(t(ail|riangle))?|right(squigarrow|harpoons|arrow(s|triangle|eq)?))|adsto)|vertneqq|floor|l(c(orner|eil)|floor|l|bracket)?|a(ngle|mbda)|rcorner|bag)|a(s(ymp|t)|ngle|pprox(eq)?|l(pha|eph)|rrownot|malg)|V(dash|vdash)|r(h(o|d)|ceil|times|i(singdotseq|ght(s(quigarrow|lice)|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(t(ail|riangle))?|rightarrows))|floor|angle|r(ceil|parenthesis|floor|bracket)|bag)|g(n(sim|eq(q)?|approx)|tr(sim|dot|eq(qless|less)|less|approx)|imel|eq(slant|q)?|vertneqq|amma|g(g)?)|Finv|xi|m(ho|i(nuso|d)|o(o|dels)|u(ltimap)?|p|e(asuredangle|rge)|aps(to|from(char)?))|b(i(n(dnasrepma|ampersand)|g(s(tar|qc(up|ap))|nplus|c(irc|u(p|rly(vee|wedge))|ap)|triangle(down|up)|interleave|o(times|dot|plus)|uplus|parallel|vee|wedge|box))|o(t|wtie|x(slash|circle|times|dot|plus|empty|ast|minus|b(slash|ox|ar)))|u(llet|mpeq)|e(cause|t(h|ween|a))|lack(square|triangle(down|left|right)?|lozenge)|a(ck(s(im(eq)?|lash)|prime|epsilon)|r(o|wedge))|bslash)|L(sh|ong(left(arrow|rightarrow)|rightarrow|maps(to|from))|eft(arrow|rightarrow)|leftarrow|ambda|bag)|Arrownot)\b + name + constant.character.math.tex + + + captures + + 1 + + name + punctuation.definition.constant.math.tex + + + match + (\\)(sum|prod|coprod|int|oint|bigcap|bigcup|bigsqcup|bigvee|bigwedge|bigodot|bigotimes|bogoplus|biguplus)\b + name + constant.character.math.tex + + + captures + + 1 + + name + punctuation.definition.constant.math.tex + + + match + (\\)(arccos|arcsin|arctan|arg|cos|cosh|cot|coth|csc|deg|det|dim|exp|gcd|hom|inf|ker|lg|lim|liminf|limsup|ln|log|max|min|pr|sec|sin|sinh|sup|tan|tanh)\b + name + constant.other.math.tex + + + begin + ((\\)Sexpr(\{)) + beginCaptures + + 1 + + name + support.function.sexpr.math.tex + + 2 + + name + punctuation.definition.function.math.tex + + 3 + + name + punctuation.section.embedded.begin.math.tex + + + contentName + support.function.sexpr.math.tex + end + (((\}))) + endCaptures + + 1 + + name + support.function.sexpr.math.tex + + 2 + + name + punctuation.section.embedded.end.math.tex + + 3 + + name + source.r + + + name + meta.embedded.line.r + patterns + + + begin + \G(?!\}) + end + (?=\}) + name + source.r + patterns + + + include + source.r + + + + + + + captures + + 1 + + name + punctuation.definition.constant.math.tex + + + match + (\\)([^a-zA-Z]|[A-Za-z]+)(?=\b|\}|\]|\^|\_) + name + constant.other.general.math.tex + + + match + (([0-9]*[\.][0-9]+)|[0-9]+) + name + constant.numeric.math.tex + + + match + «press a-z and space for greek letter»[a-zA-Z]* + name + meta.placeholder.greek.math.tex + + + + + scopeName + text.tex + uuid + 6BC8DE6F-9360-4C7E-AC3C-971385945346 + + diff --git a/SyntaxKit/Tests/Resources/Fixtures/textest.tex.txt b/SyntaxKit/Tests/Resources/Fixtures/textest.tex.txt new file mode 100644 index 0000000..77613ee --- /dev/null +++ b/SyntaxKit/Tests/Resources/Fixtures/textest.tex.txt @@ -0,0 +1,644 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% This is my macro files +%% +%% Feel free to use it or to delete it +%% +%% Anyway, this is where you put your own macros +%% (in addition to these ones +%% or in replacement as you want) +%% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%% Fonctions, notations usuelles %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\newcommand{\CoeffBinom}[2]{\binom{#1}{#2}} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%% Ensembles classiques %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\N{\mathbb{N}} +\def\Z{\mathbb{Z}} +\def\Q{\mathbb{Q}} +\def\R{\mathbb{R}} +\def\C{\mathbb{C}} +\def\U{\mathbb{U}} +\def\K{\mathbb{K}} +\def\vide{\varnothing} +\def\Premiers{\mathcal{P}} + + + + + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%% Notations pour les probas %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\def\P{P} %\def\P{\mathbb{P}} +\newcommand{\Pcond}[2]{\P_{#1}\left ( #2\right )} +\def\E{\mathbb{E}} +\def\V{\mathbb{V}} +\newcommand{\covar}[2]{\mathrm{cov}\left (#1, #2 \right)} +\newcommand{\Correlation}[2]{\mathrm{cor}\left (#1, #2 \right )} + +\newcommand{\Bernoulli}[1]{\mathcal{B}(#1)} +\newcommand{\Binomiale}[2]{\mathcal{B}(#1, #2)} +\newcommand{\Poisson}[1]{\mathcal{P}(#1)} +\newcommand{\Geometrique}[1]{\mathcal{G}(#1)} +\newcommand{\Uniforme}[1]{\mathcal{U}(#1)} +\newcommand{\Exponentielle}[1]{\mathcal{E}(#1)} +\newcommand{\Normale}[2]{\mathscr{N}(#1, #2)} + +\newcommand{\loiSuit}{\rightsquigarrow} +\def\suitVA{\hookrightarrow} + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Matrices %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%% Espaces vectoriels %%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\GLcore{\mathrm{GL}} +\def\SLcore{\mathrm{SL}} +\def\SOcore{\mathrm{SO}} + + +% cf. http://stackoverflow.com/a/1812314/1670830 +\DeclareDocumentCommand\GL{ m g }{% +{\IfNoValueTF {#2} {\GLcore\left (#1 \right )} {\GLcore_{#1}\left (#2 \right )} +}% +} + +\DeclareDocumentCommand\SL{ m g }{% +{\IfNoValueTF {#2} {\SLcore\left (#1 \right )} {\SLcore_{#1}\left (#2 \right )} +}% +} + +\DeclareDocumentCommand\SO{ m g }{% +{\IfNoValueTF {#2} {\SOcore\left (#1 \right )} {\SOcore_{#1}\left (#2 \right )} +}% +} + +\newcommand{\Mat}[2]{\mathrm{M}_{#1}\left ( #2\right)} +\newcommand{\MatRectangle}[3]{\mathrm{M}_{#1\, #2}\left ( #3\right)} +\DeclareMathOperator{\tr}{tr} + +%\newcommand{\AppliLin}[2]{\mathscr{L}\left (#1, #2\right)} +%\newcommand{\Endo}[1]{\mathscr{L}\left (#1\right)} +\newcommand{\AppliLin}[2]{\mathrm{L}\left (#1, #2\right)} +\newcommand{\Endo}[1]{\mathrm{L}\left (#1\right)} + + +\newcommand{\Orth}[1]{\mathrm{O}\left (#1\right)} +\newcommand{\MatOrth}[1]{\mathrm{O}_{#1}\left (\R\right)} + +\newcommand{\MatSym}[1]{\mathrm{S}_{#1}(\R)} +\newcommand{\MatSymPos}[1]{\mathrm{S}_{#1}^+(\R)} +\newcommand{\MatSymDefPos}[1]{\mathrm{S}_{#1}^{++}(\R)} + + +%% old way +%%\newcommand{\Transposee}[1]{\prescript{\mathrm t}{}{#1}} +\newcommand{\Transposee}[1]{\transp{#1}} +\newcommand{\TransposeeParenth}[1]{\transp{\left (#1\right )}} +\newcommand*{\transp}[2][-3mu]{\ensuremath{\mskip1mu\prescript{\smash{\mathrm t\mkern#1}}{}{\mathstrut#2}}}% + +%% new way +\newcommand{\TransposeeNew}[1]{\transpNew{#1}} +\newcommand{\TransposeeNewParenth}[1]{\transpNew{\left (#1\right )}} +\newcommand*{\transpNew}[1]{{#1}^\mathsf{T}}% + + +\newcommand{\MatApplLin}[2]{\mathrm{Mat}_{#1}\left ( #2\right)} + +\DeclareMathOperator{\Spec}{Sp} + + + +\def\Id{\mathrm{Id}} + +\DeclareMathOperator{\Vect}{Vect} + +\DeclareMathOperator{\Matt}{Mat} + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Guillemets %%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Accolades %%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\guillemets}[1]{\enquote{#1}} + +\newcommand{\accolades}[1]{\left \{#1\right \}} +\newcommand{\accoladesB}[1]{\big \{#1\big \}} +\newcommand{\accoladesBB}[1]{\Big \{#1\Big \}} +\newcommand{\accoladesBBB}[1]{\bigg \{#1\bigg \}} +\newcommand{\accoladesBBBB}[1]{\Bigg \{#1\Bigg \}} + +\newcommand{\crochets}[1]{\left [#1\right ]} +\newcommand{\crochetsB}[1]{\big [#1\big ]} +\newcommand{\crochetsBB}[1]{\Big [#1\Big ]} +\newcommand{\crochetsBBB}[1]{\bigg [#1\bigg ]} + +\newcommand{\crochetsOuv}[1]{\left ]#1\right [} +\newcommand{\crochetsOuvB}[1]{\big ]#1\big [} +\newcommand{\crochetsOuvBB}[1]{\Big ]#1\Big [} +\newcommand{\crochetsOuvBBB}[1]{\bigg ]#1\bigg [} + +\def\tq{\mathrel{}\middle|\mathrel{}} +\def\tqN{\mathrel{}|\mathrel{}} +\def\tqB{\mathrel{}\big|\mathrel{}} +\def\tqBB{\mathrel{}\Big|\mathrel{}} +\def\tqBBB{\mathrel{}\bigg|\mathrel{}} + +\newcommand{\parenth}[1]{\left (#1\right )} +\newcommand{\parenthB}[1]{\big (#1\big )} +\newcommand{\parenthBB}[1]{\Big (#1\Big )} +\newcommand{\parenthBBB}[1]{\bigg (#1\bigg )} + +\newcommand{\accoladesForAnd}[2]{\left \{\begin{array}{ll}#1\\[1mm]#2\end{array}\right.} + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%% Produit scalaire %%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\prodScal}[2]{\left(#1\,\middle\vert\,#2\right)} +\newcommand{\prodScalB}[2]{\big(#1\,\big\vert\,#2\big)} +\newcommand{\prodScalBB}[2]{\Big(#1\,\Big\vert\,#2\Big)} + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%% Intervalles %%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\intervalle}[2]{\left [#1, #2 \right ]} +\newcommand{\intervalleOF}[2]{\left ]#1, #2 \right ]} +\newcommand{\intervalleFO}[2]{\left [#1, #2 \right [} +\newcommand{\intervalleN}[2]{\left \llbracket #1, #2 \right \rrbracket} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Mon propre style de listes %%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% +% ex : \begin{myItemize}[2mm] \myItem Bonjour \myItem Hello \end{myItemize} +% + + +\usepackage{xifthen}% provides \isempty test + +\newcommand{\optarg}[1][]{% +\ifthenelse{\isempty{#1}}% +{}% if #1 is empty +{(((#1)))}% if #1 is not empty +} + +\newenvironment{myItemize}[1][] +{% +\begin{itemize}% +\ifthenelse{\isempty{#1}}% +{}% if #1 is empty +{\setlength{\itemsep}{#1}}% if #1 is not empty +} +{% +\end{itemize}% +} + + + +\def\myItem{\item[•]} +\def\myItemLong{\item[---]} +\def\myItemShort{\item[--]} + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%% Valeur absolue %%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\va}[1]{\left | #1 \right |} +\newcommand{\vaB}[1]{\big | #1 \big |} +\newcommand{\vaBB}[1]{\Big | #1 \Big |} +\newcommand{\vaBBB}[1]{\bigg | #1 \bigg |} + +\newcommand{\norm}[1]{\left \| #1 \right \|} +\newcommand{\normB}[1]{\big \| #1 \big \|} +\newcommand{\normBB}[1]{\Big \| #1 \Big \|} +\newcommand{\normBBB}[1]{\bigg \| #1 \bigg \|} + +\newcommand{\normetriple}[1]{\left | \! \left | \! \left | #1 \right | \! \right | \! \right |} +\newcommand{\normetripleB}[1]{\big | \! \big | \! \big | #1 \big | \! \big | \! \big |} +\newcommand{\normetripleBB}[1]{\Big | \! \Big| \! \Big | #1 \Big | \! \Big | \! \Big |} +\newcommand{\normetripleBBB}[1]{\bigg | \! \bigg | \! \bigg | #1 \bigg | \! \bigg | \! \bigg |} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%% Le d de dx dans les intégrales %%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\renewcommand{\d}[1]{\ensuremath{\operatorname{d}\!{#1}}} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%% Modulo %%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\modd}[1]{\ (#1)} + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Petits mots français qu'on trouve dans les formules (ou pas) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\newcommand{\ie}{\emph{ie} } +\newcommand{\et}{\text{ et }} +\newcommand{\donc}{\text{ donc }} +\newcommand{\ou}{\text{ ou }} +\newcommand{\cf}{\emph{cf.} } + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Quantificateurs %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\fa{\forall} +\def\xt{\exists} + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%% Raccourcis et petites commandes %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\longto{\mathop{\longrightarrow}} +\def\impl{\Longrightarrow} +\def\implInverse{\Longleftarrow} +\def\ssi{\iff} +\def\ssivert{\Updownarrow} + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Modification de \Im (et ker) %%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\renewcommand{\Im}{\mathrm{Im}} +\newcommand{\im}{\mathrm{Im}} +\newcommand{\Ker}{\mathrm{Ker}} +\renewcommand{\ker}{\mathrm{Ker}} +\newcommand{\rg}{\mathrm{rg}} + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Compléments xy-pic %%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%% Pour centrer les diagrammes et autres +% +% ex : \boite{} +% +\newlength\longueurdelaboite +\newcommand{\boite}[1] +{ +\settowidth{\longueurdelaboite}{#1} +\begin{minipage}{\longueurdelaboite} +#1 +\end{minipage} +} + + + +%%%%%%% Pour écrire des fonctions +% +% ex : \fonction{\R}{\R}{x}{x^2} +% +\newcommand{\fonction}[4]{ +\begin{tabular}{r@{~}c@{~}l} +$#1$&$\longrightarrow$&$#2$ \\ +$#3$&$\longmapsto$&$#4$ \\ +\end{tabular}} + + + +%%%%%%% Pour écrire des fonctions bis +% +% ex : \fonction{\R}{\R}{x}{x^2} +% +\makeatother +\newlength\longueurdudiagramme +\newcommand{\fonctionb}[4] +{% +\settowidth{\longueurdudiagramme} +{% +$\xymatrix@R=0mm{ +#1 \ar[r] & #2 \\ +#3 \ar@{|->}[r] & #4}$ +} +\begin{minipage}{\longueurdudiagramme} +$\xymatrix@R=0mm{ +#1 \ar[r] & #2 \\ +#3 \ar@{|->}[r] & #4}$ +\end{minipage} +} +\makeatletter + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Quotients %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%% Une commande pour les gros quotients +% +% ancienne commande +%%%%\newcommand{\quotient}[2]{\raisebox{4pt}{$#1$} \Big/ \raisebox{-4pt}{$#2$}} +% +% +% ex : \quotient{Z}{nZ} +% +\newcommand{\quotient}[2]{\left. \raisebox{.2em}{$#1$}\middle/\raisebox{-.2em}{$#2$}\right.} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Canonical matrixes %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% +% +% example +% \canonicalmatrix{2\text{-ième ligne}}{3\text{-ième colonne}}{A} +% +% + +\NewDocumentCommand{\canonicalmatrix}{mmm}{% +\begin{array}{@{}c@{}} +\vphantom{ +\begin{array}{@{}c@{}}\text{$#2$}\\\downarrow\end{array} +} +\\ +\hphantom{#3={}\enspace +} +\begin{pmatrix} +& & \smash[t]{ +\begin{array}[b]{@{}c@{}} +\makebox[0pt]{$#2$}\\ +\downarrow\\[-.5ex] +\vdots +\end{array} +} +\\ +& & 0 +\\ +\llap{$#3={}$\quad} +\cdots & 0 & 1 & 0 & \cdots\rlap{\quad$\leftarrow$ $#1$} +\\ +& & 0 +\\ +& & \vdots +\end{pmatrix} +\end{array}%\hphantom{\text{\enspace$\leftarrow$ $#1$-#2 row}} +} + + +\newcommand{\vecteurCanonique}[1]{% +\left ( +\begin{array}{c} +\vdots \\ +0 \\ +1 \\ +0 \\ +\vdots +\end{array} +\right ) \text{\small $\gets$ $#1$-ième ligne} +} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Démo %%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\newcommand{\demo}[1]{\noindent \textit{Démonstration.\,---\!--} #1 $\blacksquare$} +\newcommand{\demoOf}[2]{\noindent \textit{Démonstration #1.\,---\!--} #2 $\blacksquare$} + +\newcommand{\solution}[1]{\noindent \textit{Solution.\,---\!--} #1 $\blacksquare$} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%% Fonctions continues %%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\def\Cont{\mathscr{C}} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% Petit o %%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\petito}[1]{ o\left ( #1\right)} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%% Topologie %%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\bouleO}[2]{\mathring{B}\left ( #1, #2\right )} +\newcommand{\bouleF}[2]{\overline{B}\left ( #1, #2\right )} +\DeclareMathOperator{\diam}{diam} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%% Récurrence %%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\Heredite{$\text{HR}_n \impl \text{HR}_{n+1}$} +\def\HerediteForte{$(\text{HR}_k)_{k\leq n} \impl \text{HR}_{n+1}$} +\newcommand{\initRecurrence}[1]{$n=#1$} +\newcommand{\HerediteWithArgument}[1]{$\text{HR}_{#1} \impl \text{HR}_{#1+1}$} +\newcommand{\HerediteForteWithArgument}[1]{$(\text{HR}_\ell)_{\ell\leq #1} \impl \text{HR}_{#1+1}$} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%% Partie réelle et imaginaire %%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\renewcommand{\Re}{\operatorname{Re}} +\renewcommand{\Im}{\operatorname{Im}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%% Cardinal %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\DeclareMathOperator{\Card}{Card} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%% Groupes %%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\DeclareMathOperator{\Stab}{Stab} +\def\Orb{\mathscr{O}} +\newcommand{\GroupeSym}[1]{\mathfrak{S}_{#1}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%% pgcd et ppcm %%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\DeclareMathOperator{\pgcd}{pgcd} +\DeclareMathOperator{\ppcm}{ppcm} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Better underbraces for matrices %%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\newcommand{\underbracedmatrix}[2]{% +\left(\; +\smash[b]{\underbrace{ +\begin{matrix}#1\end{matrix} +}_{#2}} +\;\right) +\vphantom{\underbrace{\begin{matrix}#1\end{matrix}}_{#2}} +} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%% Equivalence %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\DeclareMathOperator{\equivaut}{\sim} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%% Signe %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\DeclareMathOperator{\signe}{\mathrm{sign}} + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%% Vertical symbols %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\def\verticalEquals{\rotatebox[origin=c]{90}{$=$}} +\def\verticalDefines{\rotatebox[origin=c]{-90}{$:=$}} +\def\verticalSsi{\big\Updownarrow} +\def\verticalImpl{\big\Downarrow} +\def\verticalLongto{\rotatebox[origin=c]{-90}{$\longto$}} +\def\verticalLeadsto{\rotatebox[origin=c]{-90}{$\leadsto$}} + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%% Better inf, max and min %%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\let\inf\relax \DeclareMathOperator*\inf{\vphantom{p}inf} +\let\max\relax \DeclareMathOperator*\max{\vphantom{p}max} +\let\min\relax \DeclareMathOperator*\min{\vphantom{p}min} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% Nice symbol for \leq and \geq %%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\renewcommand\leq{\leqslant} +\renewcommand\geq{\geqslant} + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%% Extra thin space %%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% cf. http://tex.stackexchange.com/a/332158/8323 + +\protected\def\miniSpace{% +\ifmmode +\mskip0.5\thinmuskip +\else +\ifhmode +\kern0.08334em +\fi +\fi +} \ No newline at end of file From 929049ce235c937f44b69dfe108c17599de421b4 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sat, 18 Feb 2017 12:09:34 +0100 Subject: [PATCH 095/110] linter fixes (include order) --- SyntaxKit/Pattern.swift | 2 +- SyntaxKit/Tests/AttributedParserTests.swift | 2 +- SyntaxKit/Tests/PerformanceTests.swift | 12 ++++++------ SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index ffd0193..549eaf2 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -104,7 +104,7 @@ class Pattern: NSObject { self.begin == nil && self.end == nil && (dictionary["patterns"] as? [[AnyHashable: Any]] == nil || (dictionary["patterns"] as? [[AnyHashable: Any]])!.isEmpty) { - print("Attention: pattern not recognized: \(self.name)") + print("Attention: pattern not recognized: \(String(describing: self.name))") return nil } diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 03f2bed..268c22f 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -6,8 +6,8 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import XCTest import SyntaxKit +import XCTest class AttributedParserTests: XCTestCase { diff --git a/SyntaxKit/Tests/PerformanceTests.swift b/SyntaxKit/Tests/PerformanceTests.swift index 4a94929..0a0685f 100644 --- a/SyntaxKit/Tests/PerformanceTests.swift +++ b/SyntaxKit/Tests/PerformanceTests.swift @@ -6,8 +6,8 @@ // Copyright © 2017 Sam Soffes. All rights reserved. // -import XCTest import SyntaxKit +import XCTest class PerformanceTests: XCTestCase { @@ -15,29 +15,29 @@ class PerformanceTests: XCTestCase { let manager = getBundleManager() var parser: AttributedParser! - + override func setUp() { super.setUp() let latex = manager.language(withIdentifier: "source.Latex")! let solarized = manager.theme(withIdentifier: "Solarized")! parser = AttributedParser(language: latex, theme: solarized) } - + override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } - + func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } - + func testLongTexFilePerformance() { let input = fixture("textest.tex", "txt") self.measure { _ = self.parser.attributedString(for: input) } } - + } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 81a6a29..f2951c7 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -7,8 +7,8 @@ // import Foundation -import XCTest import SyntaxKit +import XCTest class SwiftBaselineHighlightingTests: XCTestCase { From 5b140050badf812c5cae34d640de8c6542f27c58 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 12 Mar 2017 12:26:47 +0100 Subject: [PATCH 096/110] use xcode 8.2 --- .../xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SyntaxKit-macOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SyntaxKit-watchOS.xcscheme | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index 80d7e01..b536613 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 6 Jun 2017 00:01:33 +0200 Subject: [PATCH 097/110] Use swiftlint and squish warnings --- .swiftlint.yml | 43 +++++++ SyntaxKit.xcodeproj/project.pbxproj | 17 +++ SyntaxKit/AttributedParsingOperation.swift | 2 +- SyntaxKit/BundleManager.swift | 18 +-- SyntaxKit/Capture.swift | 2 +- SyntaxKit/CaptureCollection.swift | 2 +- SyntaxKit/Color.swift | 28 ++++- SyntaxKit/Language.swift | 2 +- SyntaxKit/Parser.swift | 63 ++++++---- SyntaxKit/Pattern.swift | 115 +++++++++--------- SyntaxKit/ReferenceManager.swift | 8 +- SyntaxKit/Repository.swift | 2 +- SyntaxKit/Result.swift | 4 +- SyntaxKit/ResultSet.swift | 4 +- SyntaxKit/ScopedString.swift | 13 +- SyntaxKit/Tests/AttributedParserTests.swift | 27 ++-- SyntaxKit/Tests/IncrementalParsingTests.swift | 77 ++++++------ SyntaxKit/Tests/LanguageTests.swift | 62 +++++----- SyntaxKit/Tests/ParserTests.swift | 58 +++++---- SyntaxKit/Tests/PerformanceTests.swift | 29 ++--- SyntaxKit/Tests/ScopedStringTests.swift | 12 +- .../SwiftBaselineHighlightingTests.swift | 65 +++++----- SyntaxKit/Tests/TestHelper.swift | 59 +++------ SyntaxKit/Tests/ThemeTests.swift | 38 +++--- SyntaxKit/Theme.swift | 6 +- 25 files changed, 424 insertions(+), 332 deletions(-) create mode 100644 .swiftlint.yml diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..64b491c --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,43 @@ +disabled_rules: + - line_length + +opt_in_rules: + - attributes + - closure_end_indentation + - closure_spacing + - conditional_returns_on_newline + - empty_count + - explicit_init + - explicit_top_level_acl + - explicit_type_interface + - fatal_error_message + - file_header + - first_where + - force_unwrapping + - implicit_return + - implicitly_unwrapped_optional + - missing_docs + - nimble_operator + - no_extension_access_modifier + - number_separator + - object_literal + - operator_usage_whitespace + - overridden_super_call + - private_outlet + - prohibited_super_call + - redundant_nil_coalescing + - redundant_nil_coalesing + - sorted_imports + - switch_case_on_newline + +file_header: + required_pattern: | + \/\/ + \/\/ .*?\.swift + \/\/ SyntaxKit + \/\/( + \/\/ .* + \/\/)? + \/\/ Created by .*? on \d{1,2}\/\d{1,2}\/\d{2}\. + \/\/ Copyright © \d{4}(-\d{4})? .*?\. All rights reserved\. + \/\/ diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 2f2d136..2e569da 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -511,6 +511,7 @@ 2122A6DA1B22B9320006409B /* Frameworks */, 2122A6DB1B22B9320006409B /* Headers */, 2122A6DC1B22B9320006409B /* Resources */, + 8CDFB7351EE5BA1200CBB1D1 /* ShellScript */, ); buildRules = ( ); @@ -698,6 +699,22 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 8CDFB7351EE5BA1200CBB1D1 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ $CLANG_STATIC_ANALYZER_MODE ]; then\n if which swiftlint >/dev/null; then\n swiftlint\n else\n echo \"Please install SwiftLint\"\n fi\nfi"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 211826D51D257A71003F2BF2 /* Sources */ = { isa = PBXSourcesBuildPhase; diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index e942cd5..c8c2769 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -19,7 +19,7 @@ // /// Represents one change (insertion or deletion) between two strings -struct Diff { +internal struct Diff { // MARK: - Properties diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index fee3c4d..398982b 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -15,7 +15,7 @@ open class BundleManager { public enum TextMateFileType { - case Language, Theme + case language, theme } // MARK: - Types @@ -35,7 +35,7 @@ open class BundleManager { /// /// - note: Setting it to false will not invalidate or purge the cache. This /// has to be done separately using clearLanguageCache. - open var languageCaching = true + open var languageCaching: Bool = true open static var defaultManager: BundleManager? @@ -52,10 +52,10 @@ open class BundleManager { /// - parameter callback: The callback used to find the location of the /// textmate files. open class func initializeDefaultManager(with callback: @escaping BundleLocationCallback) { - if defaultManager == nil { - defaultManager = BundleManager(callback: callback) + if let manager = defaultManager { + manager.bundleCallback = callback } else { - defaultManager!.bundleCallback = callback + defaultManager = BundleManager(callback: callback) } } @@ -87,7 +87,7 @@ open class BundleManager { return theme } - guard let dictURL = self.bundleCallback(identifier, .Theme), + guard let dictURL = self.bundleCallback(identifier, .theme), let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], let newTheme = Theme(dictionary: plist) else { return nil @@ -109,10 +109,10 @@ open class BundleManager { func loadRawLanguage(withIdentifier identifier: String) -> Language? { let indexOfStoredLanguage = self.dependencies.index { (lang: Language) in lang.scopeName == identifier } - if indexOfStoredLanguage != nil { - return self.dependencies[indexOfStoredLanguage!] + if let index = indexOfStoredLanguage { + return self.dependencies[index] } else { - guard let dictURL = self.bundleCallback(identifier, .Language), + guard let dictURL = self.bundleCallback(identifier, .language), let plist = NSDictionary(contentsOf: dictURL) as? [String: Any], let newLanguage = Language(dictionary: plist, manager: self) else { return nil diff --git a/SyntaxKit/Capture.swift b/SyntaxKit/Capture.swift index d048954..839f771 100644 --- a/SyntaxKit/Capture.swift +++ b/SyntaxKit/Capture.swift @@ -8,7 +8,7 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -struct Capture { +internal struct Capture { // MARK: - Properties diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index 6aea7f5..a59b7b5 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -8,7 +8,7 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -struct CaptureCollection { +internal struct CaptureCollection { // MARK: - Properties diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index c568646..fc36c9c 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -1,6 +1,6 @@ // // Color.swift -// X +// SyntaxKit // // Created by Sam Soffes on 4/28/15. // Copyright © 2015 Sam Soffes. All rights reserved. @@ -18,6 +18,32 @@ #else import UIKit.UIColor public typealias ColorType = UIColor + + extension Color { + var redComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(&value, green: nil, blue: nil, alpha: nil) + return value + } + + var greenComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: &value, blue: nil, alpha: nil) + return value + } + + var blueComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: nil, blue: &value, alpha: nil) + return value + } + + var alphaComponent: CGFloat { + var value: CGFloat = 0.0 + getRed(nil, green: nil, blue: nil, alpha: &value) + return value + } + } #endif public typealias Color = ColorType diff --git a/SyntaxKit/Language.swift b/SyntaxKit/Language.swift index 64e1f91..b23b9ee 100644 --- a/SyntaxKit/Language.swift +++ b/SyntaxKit/Language.swift @@ -21,7 +21,7 @@ public struct Language { let referenceManager: ReferenceManager let repository: Repository - static let globalScope = "GLOBAL" + static let globalScope: String = "GLOBAL" // MARK: - Initializers diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 63184ac..9ee462f 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -24,9 +24,9 @@ open class Parser { /// String that is used in parse(in:). May already contain lexical /// information from previous calls to parse for incremental parsing. /// Stores the recognized lexical scopes after a successful call to parse. - var toParse = ScopedString(string: "") + var toParse: ScopedString = ScopedString(string: "") /// Set to true to abort the parsing pass - var aborted = false + var aborted: Bool = false // MARK: - Initializers @@ -132,31 +132,39 @@ open class Parser { let bestMatchForMiddle = match(pattern.subpatterns, in: range) - if pattern.end != nil { - let endMatchResult = self.match(pattern.end!, in: range, captures: pattern.endCaptures) - if endMatchResult != nil && (bestMatchForMiddle == nil || bestMatchForMiddle != nil && - (!pattern.applyEndPatternLast && endMatchResult!.range.location <= bestMatchForMiddle!.match.range.location || endMatchResult!.range.location < bestMatchForMiddle!.match.range.location)) { - result.add(endMatchResult!) + if let patternEnd = pattern.end, + let endMatchResult = self.match(patternEnd, in: range, captures: pattern.endCaptures) { + if let middleMatch = bestMatchForMiddle { + if !pattern.applyEndPatternLast && endMatchResult.range.location <= middleMatch.match.range.location || endMatchResult.range.location < middleMatch.match.range.location { + result.add(endMatchResult) + return result + } + } else { + result.add(endMatchResult) return result } } - if bestMatchForMiddle != nil { + if let middleMatch = bestMatchForMiddle { let resultForMiddle: ResultSet? - if bestMatchForMiddle!.pattern.match != nil { - resultForMiddle = bestMatchForMiddle!.match + if middleMatch.pattern.match != nil { + resultForMiddle = middleMatch.match } else { - resultForMiddle = matchAfterBegin(of: bestMatchForMiddle!.pattern, beginResults: bestMatchForMiddle!.match) + resultForMiddle = matchAfterBegin(of: middleMatch.pattern, beginResults: middleMatch.match) } - if resultForMiddle == nil || resultForMiddle!.range.length == 0 { + if let middleResult = resultForMiddle { + if middleResult.range.length != 0 { + result.add(middleResult) + let newStart = NSMaxRange(middleResult.range) + range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) + lineEnd = max(lineEnd, newStart) + } else { + break + } + } else { break } - - result.add(resultForMiddle!) - let newStart = NSMaxRange(resultForMiddle!.range) - range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) - lineEnd = max(lineEnd, newStart) } else { break } @@ -185,9 +193,16 @@ open class Parser { let currentMatch = self.firstMatch(of: pattern, in: range) if currentMatch?.match.range.location == range.location { return currentMatch - } else if currentMatch != nil && (bestResult == nil || currentMatch != nil && currentMatch!.match.range.location < bestResult!.match.range.location) { - bestResult = currentMatch - interestingBounds.length = currentMatch!.match.range.location - interestingBounds.location + } else if let currMatch = currentMatch { + if let best = bestResult { + if currMatch.match.range.location < best.match.range.location { + bestResult = currentMatch + interestingBounds.length = currMatch.match.range.location - interestingBounds.location + } + } else { + bestResult = currentMatch + interestingBounds.length = currMatch.match.range.location - interestingBounds.location + } } } return bestResult @@ -233,8 +248,8 @@ open class Parser { } let result = ResultSet(startingRange: endResults.range) - if pattern.name != nil { - result.add(Result(identifier: pattern.name!, range: NSUnionRange(begin.range, endResults.range))) + if let patternName = pattern.name { + result.add(Result(identifier: patternName, range: NSUnionRange(begin.range, endResults.range))) } result.add(Scope(identifier: pattern.name ?? "", range: NSRange(location: begin.range.location + begin.range.length, length: NSUnionRange(begin.range, endResults.range).length - begin.range.length), attribute: pattern)) result.add(begin) @@ -261,8 +276,8 @@ open class Parser { } let resultSet = ResultSet(startingRange: result.range) - if baseSelector != nil { - resultSet.add(Result(identifier: baseSelector!, range: result.range)) + if let base = baseSelector { + resultSet.add(Result(identifier: base, range: result.range)) } if let captures = captures { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 549eaf2..ca1aa0f 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -20,7 +20,7 @@ // @objc(SKPattern) -class Pattern: NSObject { +internal class Pattern: NSObject { // MARK: - Properties @@ -42,10 +42,10 @@ class Pattern: NSObject { fileprivate var _beginCaptures: CaptureCollection? fileprivate var _end: NSRegularExpression? fileprivate var _endCaptures: CaptureCollection? - fileprivate var _applyEndPatternLast = false + fileprivate var _applyEndPatternLast: Bool = false fileprivate weak var _parent: Pattern? - fileprivate let debug = true + fileprivate let debug: Bool = true // MARK: - Initializers @@ -102,10 +102,16 @@ class Pattern: NSObject { if self.match == nil && self.begin == nil && - self.end == nil && - (dictionary["patterns"] as? [[AnyHashable: Any]] == nil || (dictionary["patterns"] as? [[AnyHashable: Any]])!.isEmpty) { + self.end == nil { + if let patterns = dictionary["patterns"] as? [[AnyHashable: Any]] { + if patterns.isEmpty { + print("Attention: pattern not recognized: \(String(describing: self.name))") + return nil + } + } else { print("Attention: pattern not recognized: \(String(describing: self.name))") return nil + } } if let array = dictionary["patterns"] as? [[AnyHashable: Any]] { @@ -132,24 +138,20 @@ class Pattern: NSObject { } } -enum ReferenceType { - case toRepository - case toSelf - case toBase - case toForeign - case toForeignRepository - case resolved -} - -class Include: Pattern { +internal class Include: Pattern { // MARK: - Properties - var type: ReferenceType { return _type } + enum ReferenceType { + case toRepository (repositoryRef: String) + case toSelf + case toBase + case toForeign (languageRef: String) + case toForeignRepository (repositoryRef: String, languageRef: String) + case resolved + } - fileprivate var _type: ReferenceType - fileprivate let repositoryRef: String? - fileprivate let languageRef: String? + fileprivate var type: ReferenceType fileprivate var associatedRepository: Repository? // MARK: - Initializers @@ -157,36 +159,30 @@ class Include: Pattern { init(reference: String, in repository: Repository? = nil, parent: Pattern?, manager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { - self._type = .toRepository - self.repositoryRef = reference.substring(from: reference.characters.index(after: reference.startIndex)) - self.languageRef = nil + self.type = .toRepository(repositoryRef: reference.substring(from: reference.characters.index(after: reference.startIndex))) } else if reference == "$self" { - self._type = .toSelf - self.repositoryRef = nil - self.languageRef = nil + self.type = .toSelf } else if reference == "$base" { - self._type = .toBase - self.repositoryRef = nil - self.languageRef = nil + self.type = .toBase } else if reference.contains("#") { - self._type = .toForeignRepository - self.repositoryRef = reference.substring(from: reference.range(of: "#")!.upperBound) - self.languageRef = reference.substring(to: reference.range(of: "#")!.lowerBound) - _ = manager.loadRawLanguage(withIdentifier: languageRef!) + if let hashRange = reference.range(of: "#") { + let languagePart = reference.substring(to: hashRange.lowerBound) + self.type = .toForeignRepository(repositoryRef: reference.substring(from: hashRange.upperBound), languageRef: languagePart) + _ = manager.loadRawLanguage(withIdentifier: languagePart) + } else { + assert(false) + type = .toSelf + } } else { - self._type = .toForeign - self.repositoryRef = nil - self.languageRef = reference - _ = manager.loadRawLanguage(withIdentifier: languageRef!) + self.type = .toForeign(languageRef: reference) + _ = manager.loadRawLanguage(withIdentifier: reference) } super.init() _parent = parent } init(include: Include, parent: Pattern?) { - self._type = include.type - self.repositoryRef = include.repositoryRef - self.languageRef = include.languageRef + self.type = include.type self.associatedRepository = include.associatedRepository super.init(pattern: include, parent: parent) } @@ -195,36 +191,43 @@ class Include: Pattern { func resolveInternalReference(with repository: Repository, in language: Language) { let pattern: Pattern? - if type == .toRepository { - pattern = (associatedRepository ?? repository)[repositoryRef!] - } else if type == .toSelf { + switch type { + case .toRepository(let repositoryRef): + pattern = (associatedRepository ?? repository)[repositoryRef] + case .toSelf: pattern = language.pattern - } else { + default: return } - if pattern != nil { - self.replace(with: pattern!) + if let pat = pattern { + self.replace(with: pat) } - _type = .resolved + type = .resolved } func resolveExternalReference(from thisLanguage: Language, in languages: [String: Language], baseName: String?) { let pattern: Pattern? - if type == .toBase { - pattern = languages[baseName!]!.pattern - } else if type == .toForeignRepository { - pattern = languages[languageRef!]?.repository[repositoryRef!] - } else if type == .toForeign { - pattern = languages[languageRef!]?.pattern - } else { + switch type { + case .toBase: + if let base = baseName { + pattern = languages[base]?.pattern + } else { + assert(false) + pattern = nil + } + case .toForeignRepository(let repositoryRef, let languageRef): + pattern = languages[languageRef]?.repository[repositoryRef] + case .toForeign(let languageRef): + pattern = languages[languageRef]?.pattern + default: return } - if pattern != nil { - self.replace(with: pattern!) + if let pat = pattern { + self.replace(with: pat) } - _type = .resolved + type = .resolved } // MARK: - Private diff --git a/SyntaxKit/ReferenceManager.swift b/SyntaxKit/ReferenceManager.swift index 6712e2f..5cd9d34 100644 --- a/SyntaxKit/ReferenceManager.swift +++ b/SyntaxKit/ReferenceManager.swift @@ -14,7 +14,7 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -class ReferenceManager { +internal class ReferenceManager { // MARK: - Properties @@ -30,10 +30,14 @@ class ReferenceManager { // MARK: - Pattern Creation and Resolution func patterns(for patterns: [[AnyHashable: Any]], in repository: Repository?, caller: Pattern?) -> [Pattern] { + guard let manager = bundleManager else { + assert(false) + return [] + } var results: [Pattern] = [] for rawPattern in patterns { if let include = rawPattern["include"] as? String { - let reference = Include(reference: include, in: repository, parent: caller, manager: bundleManager!) + let reference = Include(reference: include, in: repository, parent: caller, manager: manager) self.includes.append(reference) results.append(reference) } else if let pattern = Pattern(dictionary: rawPattern, parent: caller, with: repository, with: self) { diff --git a/SyntaxKit/Repository.swift b/SyntaxKit/Repository.swift index d64ff91..c6e50b2 100644 --- a/SyntaxKit/Repository.swift +++ b/SyntaxKit/Repository.swift @@ -9,7 +9,7 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -class Repository { +internal class Repository { // MARK: - Properties diff --git a/SyntaxKit/Result.swift b/SyntaxKit/Result.swift index 73d3ce5..abf3504 100644 --- a/SyntaxKit/Result.swift +++ b/SyntaxKit/Result.swift @@ -8,7 +8,7 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -struct Result: Equatable { +internal struct Result: Equatable { // MARK: - Properties @@ -25,7 +25,7 @@ struct Result: Equatable { } } -func == (lhs: Result, rhs: Result) -> Bool { +internal func == (lhs: Result, rhs: Result) -> Bool { return lhs.patternIdentifier == rhs.patternIdentifier && lhs.range.toRange() == rhs.range.toRange() } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index c2c4f09..c9d7af3 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -8,7 +8,7 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -class ResultSet { +internal class ResultSet { // MARK: - Properties @@ -17,7 +17,7 @@ class ResultSet { /// associated with the contained results. var range: NSRange { return _range } - fileprivate var _results = [Result]() + fileprivate var _results: [Result] = [] fileprivate var _range: NSRange // MARK: - Initializers diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index 3a703f1..fc0e1f9 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -71,7 +71,7 @@ extension NSRange { /// agreement, please respect ;). typealias Scope = Result -struct ScopedString { +internal struct ScopedString { // MARK: - Properties @@ -160,11 +160,10 @@ struct ScopedString { let indexRange = NSRange(location: index, length: 0) for i in stride(from: (levels.count - 1), through: 0, by: -1) { let level = levels[i] - let theScope = findScopeIntersection(with: indexRange, at: level) - if theScope != nil { + if let theScope = findScopeIntersection(with: indexRange, at: level) { if foundScope { return scope - } else if theScope! == scope { + } else if theScope == scope { foundScope = true } } @@ -175,10 +174,8 @@ struct ScopedString { func level(for scope: Scope) -> Int { for i in 0 ..< levels.count { let level = levels[i] - for currentScope in level { - if scope == currentScope { - return i + 1 - } + if level.contains(scope) { + return i + 1 } } if scope == baseScope { diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index 268c22f..c20dcde 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -1,6 +1,6 @@ // // AttributedParserTests.swift -// SyntaxKitTests +// SyntaxKit // // Created by Sam Soffes on 9/19/14. // Copyright © 2014-2015 Sam Soffes. All rights reserved. @@ -9,27 +9,32 @@ import SyntaxKit import XCTest -class AttributedParserTests: XCTestCase { +internal class AttributedParserTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() - var parser: AttributedParser! + fileprivate let manager: BundleManager = getBundleManager() + fileprivate var parser: AttributedParser? // MARK: - Tests override func setUp() { super.setUp() - let yaml = manager.language(withIdentifier: "source.YAML")! - parser = AttributedParser(language: yaml, theme: simpleTheme()) + if let yaml = manager.language(withIdentifier: "source.YAML"), + let theme = simpleTheme() { + parser = AttributedParser(language: yaml, theme: theme) + } else { + XCTFail() + } + } func testParsing() { - let string = parser.attributedString(for: "title: Hello World\ncount: 42\n") + let string = parser?.attributedString(for: "title: Hello World\ncount: 42\n") - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributes(at: 0, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "red"] as NSDictionary, string.attributes(at: 7, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "blue"] as NSDictionary, string.attributes(at: 19, effectiveRange: nil) as NSDictionary) - XCTAssertEqual(["color": "purple"] as NSDictionary, string.attributes(at: 25, effectiveRange: nil) as NSDictionary) + XCTAssertEqual(["color": "blue"], string?.attributes(at: 0, effectiveRange: nil) as NSDictionary?) + XCTAssertEqual(["color": "red"], string?.attributes(at: 7, effectiveRange: nil) as NSDictionary?) + XCTAssertEqual(["color": "blue"], string?.attributes(at: 19, effectiveRange: nil) as NSDictionary?) + XCTAssertEqual(["color": "purple"], string?.attributes(at: 25, effectiveRange: nil) as NSDictionary?) } } diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index 81ef3be..a230376 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -6,35 +6,30 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import XCTest import SyntaxKit +import XCTest -class IncrementalParsingTests: XCTestCase { +internal class IncrementalParsingTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() - var parsingOperation: AttributedParsingOperation! - var theme: Theme! - var language: Language! - var totalRange: NSRange? - var input = "" + fileprivate let manager: BundleManager = getBundleManager() + fileprivate var parsingOperation: AttributedParsingOperation? + fileprivate var totalRange: NSRange? + fileprivate var input: String = "" // MARK: - Tests override func setUp() { super.setUp() - language = manager.language(withIdentifier: "Source.swift")! - theme = manager.theme(withIdentifier: "tomorrow")! - } func testEdits() { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() - parsingOperation.main() - XCTAssertEqual(totalRange!, NSRange(location: 0, length: (input as NSString).length)) + parsingOperation?.main() + XCTAssertEqual(totalRange, NSRange(location: 0, length: (input as NSString).length)) assertInsertion("i", location: 162, expectedRange: NSRange(location: 159, length: 5)) @@ -47,7 +42,7 @@ class IncrementalParsingTests: XCTestCase { input = "Only this!" parsingOperation = getParsingOperation() - parsingOperation.main() + parsingOperation?.main() assertDeletion(NSRange(location: 9, length: 1), expectedRange: NSRange(location: 0, length: 9)) } @@ -56,7 +51,7 @@ class IncrementalParsingTests: XCTestCase { input = "// test.swift\n/**" parsingOperation = getParsingOperation() - parsingOperation.main() + parsingOperation?.main() XCTAssertEqual(totalRange, NSRange(location: 0, length: 17)) assertDeletion(NSRange(location: 2, length: 1), expectedRange: NSRange(location: 0, length: 13)) @@ -70,7 +65,7 @@ class IncrementalParsingTests: XCTestCase { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() - parsingOperation.main() + parsingOperation?.main() self.measure { self.assertInsertion("Tests", location: 239, expectedRange: NSRange(location: 230, length: 24)) @@ -83,7 +78,7 @@ class IncrementalParsingTests: XCTestCase { input = fixture("swifttest.swift", "txt") parsingOperation = getParsingOperation() - parsingOperation.main() + parsingOperation?.main() self.measure { self.assertDeletion(NSRange(location: 139, length: 1), expectedRange: NSRange(location: 139, length: 22)) @@ -94,33 +89,47 @@ class IncrementalParsingTests: XCTestCase { // MARK: - Helpers - fileprivate func getParsingOperation() -> AttributedParsingOperation { - return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], _: AttributedParsingOperation) in - for result in results { - if self.totalRange == nil { - self.totalRange = result.range - } else { - self.totalRange = NSUnionRange(self.totalRange!, result.range) + fileprivate func getParsingOperation() -> AttributedParsingOperation? { + if let language = manager.language(withIdentifier: "Source.swift"), + let theme = manager.theme(withIdentifier: "tomorrow") { + return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], _: AttributedParsingOperation) in + for result in results { + if let range = self.totalRange { + self.totalRange = NSUnionRange(range, result.range) + } else { + self.totalRange = result.range + } } } + } else { + XCTFail() + return nil } } fileprivate func assertInsertion(_ string: String, location: Int, expectedRange expected: NSRange) { input = replace(NSRange(location: location, length: 0), in: input, with: string) - parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) - - totalRange = nil - parsingOperation.main() - XCTAssertEqual(totalRange!, expected) + if let previousOperation = parsingOperation { + parsingOperation = AttributedParsingOperation(string: input, previousOperation: previousOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) + + totalRange = nil + parsingOperation?.main() + XCTAssertEqual(totalRange, expected) + } else { + XCTFail() + } } fileprivate func assertDeletion(_ range: NSRange, expectedRange expected: NSRange) { input = replace(range, in: input, with: "") - parsingOperation = AttributedParsingOperation(string: input, previousOperation: parsingOperation, changeIsInsertion: false, changedRange: range) - - totalRange = nil - parsingOperation.main() - XCTAssertEqual(totalRange, expected) + if let previousOperation = parsingOperation { + parsingOperation = AttributedParsingOperation(string: input, previousOperation: previousOperation, changeIsInsertion: false, changedRange: range) + + totalRange = nil + parsingOperation?.main() + XCTAssertEqual(totalRange, expected) + } else { + XCTFail() + } } } diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 1169053..88be620 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -6,45 +6,51 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -import XCTest @testable import SyntaxKit +import XCTest -class LanguageTests: XCTestCase { +internal class LanguageTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() + fileprivate let manager: BundleManager = getBundleManager() // MARK: - Tests func testYaml() { - let yaml = manager.language(withIdentifier: "source.YAML")! - XCTAssertEqual(UUID(uuidString: "B0C44228-4F1F-11DA-AFF2-000A95AF0064"), yaml.uuid) - XCTAssertEqual("YAML", yaml.name) - XCTAssertEqual("source.yaml", yaml.scopeName) - - XCTAssertEqual("meta.embedded.line.ruby", yaml.pattern.subpatterns[0].name) - XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.pattern.subpatterns[0].beginCaptures?[0]?.name) - XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.pattern.subpatterns[0].endCaptures?[0]?.name) - XCTAssertEqual("punctuation.definition.comment.ruby", yaml.pattern.subpatterns[0].subpatterns[0].captures?[1]?.name) - XCTAssertEqual("string.unquoted.block.yaml", yaml.pattern.subpatterns[1].name) - XCTAssertEqual("punctuation.definition.entry.yaml", yaml.pattern.subpatterns[1].beginCaptures?[2]?.name) - XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.pattern.subpatterns[1].beginCaptures?[5]?.name) - XCTAssertEqual("constant.numeric.yaml", yaml.pattern.subpatterns[2].name) - - let pattern = yaml.pattern.subpatterns[3] - XCTAssertEqual("string.unquoted.yaml", pattern.name) - XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) + if let yaml = manager.language(withIdentifier: "source.YAML") { + XCTAssertEqual(UUID(uuidString: "B0C44228-4F1F-11DA-AFF2-000A95AF0064"), yaml.uuid) + XCTAssertEqual("YAML", yaml.name) + XCTAssertEqual("source.yaml", yaml.scopeName) + + XCTAssertEqual("meta.embedded.line.ruby", yaml.pattern.subpatterns[0].name) + XCTAssertEqual("punctuation.definition.embedded.begin.ruby", yaml.pattern.subpatterns[0].beginCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.embedded.end.ruby", yaml.pattern.subpatterns[0].endCaptures?[0]?.name) + XCTAssertEqual("punctuation.definition.comment.ruby", yaml.pattern.subpatterns[0].subpatterns[0].captures?[1]?.name) + XCTAssertEqual("string.unquoted.block.yaml", yaml.pattern.subpatterns[1].name) + XCTAssertEqual("punctuation.definition.entry.yaml", yaml.pattern.subpatterns[1].beginCaptures?[2]?.name) + XCTAssertEqual("punctuation.separator.key-value.yaml", yaml.pattern.subpatterns[1].beginCaptures?[5]?.name) + XCTAssertEqual("constant.numeric.yaml", yaml.pattern.subpatterns[2].name) + + let pattern = yaml.pattern.subpatterns[3] + XCTAssertEqual("string.unquoted.yaml", pattern.name) + XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) + } else { + XCTFail() + } } func testSwift() { - let swift = manager.language(withIdentifier: "source.swift")! - XCTAssertEqual(UUID(uuidString: "D133338A-DEED-4ECC-9852-A392C44D10AC"), swift.uuid) - XCTAssertEqual("Swift", swift.name) - XCTAssertEqual("source.swift", swift.scopeName) - - XCTAssertEqual("comment.line.shebang.swift", swift.pattern.subpatterns[0].name) - XCTAssertEqual(4, swift.pattern.subpatterns[1].subpatterns.count) - XCTAssertEqual("comment.line.double-slash.swift", swift.pattern.subpatterns[1].subpatterns[3].subpatterns[0].name) + if let swift = manager.language(withIdentifier: "source.swift") { + XCTAssertEqual(UUID(uuidString: "D133338A-DEED-4ECC-9852-A392C44D10AC"), swift.uuid) + XCTAssertEqual("Swift", swift.name) + XCTAssertEqual("source.swift", swift.scopeName) + + XCTAssertEqual("comment.line.shebang.swift", swift.pattern.subpatterns[0].name) + XCTAssertEqual(4, swift.pattern.subpatterns[1].subpatterns.count) + XCTAssertEqual("comment.line.double-slash.swift", swift.pattern.subpatterns[1].subpatterns[3].subpatterns[0].name) + } else { + XCTFail() + } } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 56cba86..3a5b30b 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -6,22 +6,25 @@ // Copyright © 2015 Sam Soffes. All rights reserved. // -import XCTest import SyntaxKit +import XCTest -class ParserTests: XCTestCase { +internal class ParserTests: XCTestCase { // MARK: - Properties - var parser: Parser! - let manager = getBundleManager() + fileprivate var parser: Parser? + fileprivate let manager: BundleManager = getBundleManager() // MARK: - Tests override func setUp() { super.setUp() - let yaml = manager.language(withIdentifier: "source.YAML")! - parser = Parser(language: yaml) + if let yaml = manager.language(withIdentifier: "source.YAML") { + parser = Parser(language: yaml) + } else { + XCTFail() + } } func testParsingBeginEnd() { @@ -29,18 +32,18 @@ class ParserTests: XCTestCase { var punctuationBegin: NSRange? var punctuationEnd: NSRange? - parser.parse("title: \"Hello World\"\n") { (result: (scope: String, range: NSRange)) in - if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { - stringQuoted = result.range - } + parser?.parse("title: \"Hello World\"\n") { (result: (scope: String, range: NSRange)) in + if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { + stringQuoted = result.range + } - if punctuationBegin == nil && result.scope.hasPrefix("punctuation.definition.string.begin") { - punctuationBegin = result.range - } + if punctuationBegin == nil && result.scope.hasPrefix("punctuation.definition.string.begin") { + punctuationBegin = result.range + } - if punctuationEnd == nil && result.scope.hasPrefix("punctuation.definition.string.end") { - punctuationEnd = result.range - } + if punctuationEnd == nil && result.scope.hasPrefix("punctuation.definition.string.end") { + punctuationEnd = result.range + } } XCTAssertEqual(NSRange(location: 7, length: 13), stringQuoted) @@ -51,24 +54,27 @@ class ParserTests: XCTestCase { func testParsingBeginEndGarbage() { var stringQuoted: NSRange? - parser.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (result: (scope: String, range: NSRange)) in - if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { - stringQuoted = result.range - } + parser?.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (result: (scope: String, range: NSRange)) in + if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { + stringQuoted = result.range + } } XCTAssertEqual(NSRange(location: 39, length: 4), stringQuoted) } func testParsingGarbage() { - parser.parse("") { _ in } - parser.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _ in } + parser?.parse("") { _ in } + parser?.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _ in } } func testRuby() { - let ruby = manager.language(withIdentifier: "source.Ruby")! - parser = Parser(language: ruby) - let input = fixture("test.rb", "txt") - parser.parse(input, match: { _ in return }) + if let ruby = manager.language(withIdentifier: "source.Ruby") { + parser = Parser(language: ruby) + let input = fixture("test.rb", "txt") + parser?.parse(input, match: { _ in return }) + } else { + XCTFail() + } } } diff --git a/SyntaxKit/Tests/PerformanceTests.swift b/SyntaxKit/Tests/PerformanceTests.swift index 0a0685f..b08756a 100644 --- a/SyntaxKit/Tests/PerformanceTests.swift +++ b/SyntaxKit/Tests/PerformanceTests.swift @@ -2,41 +2,34 @@ // PerformanceTests.swift // SyntaxKit // -// Created by Alexander Hedges on 05.01.17. +// Created by Alexander Hedges on 05/01/17. // Copyright © 2017 Sam Soffes. All rights reserved. // import SyntaxKit import XCTest -class PerformanceTests: XCTestCase { +internal class PerformanceTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() - var parser: AttributedParser! + fileprivate let manager: BundleManager = getBundleManager() + fileprivate var parser: AttributedParser? override func setUp() { super.setUp() - let latex = manager.language(withIdentifier: "source.Latex")! - let solarized = manager.theme(withIdentifier: "Solarized")! - parser = AttributedParser(language: latex, theme: solarized) - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + if let latex = manager.language(withIdentifier: "source.Latex"), + let solarized = manager.theme(withIdentifier: "Solarized") { + parser = AttributedParser(language: latex, theme: solarized) + } else { + XCTFail() + } } func testLongTexFilePerformance() { let input = fixture("textest.tex", "txt") self.measure { - _ = self.parser.attributedString(for: input) + _ = self.parser?.attributedString(for: input) } } diff --git a/SyntaxKit/Tests/ScopedStringTests.swift b/SyntaxKit/Tests/ScopedStringTests.swift index 4c94028..15a3baf 100644 --- a/SyntaxKit/Tests/ScopedStringTests.swift +++ b/SyntaxKit/Tests/ScopedStringTests.swift @@ -6,18 +6,10 @@ // Copyright © 2016 Alexander Hedges. All rights reserved. // -import XCTest @testable import SyntaxKit +import XCTest -class ScopedStringTests: XCTestCase { - - override func setUp() { - super.setUp() - } - - override func tearDown() { - super.tearDown() - } +internal class ScopedStringTests: XCTestCase { func testScopesString() { var newScopedString = ScopedString(string: "Test") diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index f2951c7..afc17b4 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -10,53 +10,58 @@ import Foundation import SyntaxKit import XCTest -class SwiftBaselineHighlightingTests: XCTestCase { +internal class SwiftBaselineHighlightingTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() - var parser: AttributedParser! + let manager: BundleManager = getBundleManager() + var parser: AttributedParser? // MARK: - Tests override func setUp() { super.setUp() - let swift = manager.language(withIdentifier: "source.Swift")! - let solarized = manager.theme(withIdentifier: "Solarized")! - parser = AttributedParser(language: swift, theme: solarized) + if let swift = manager.language(withIdentifier: "source.Swift"), + let solarized = manager.theme(withIdentifier: "Solarized") { + parser = AttributedParser(language: swift, theme: solarized) + } else { + XCTFail() + } } func testColors() { let input = fixture("swifttest.swift", "txt") - let string = parser.attributedString(for: input) - - // line comment - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - - // block comment -// print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - - // string literal -// print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - var stringRange = NSRange() - assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) - XCTAssertEqual(stringRange.length, 4) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - - // number literal - var numberRange = NSRange() -// print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) - assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) - XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) + if let string = parser?.attributedString(for: input) { + // line comment + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + + // block comment + // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + + // string literal + // print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + var stringRange = NSRange() + assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) + XCTAssertEqual(stringRange.length, 4) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + + // number literal + var numberRange = NSRange() + // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) + assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) + XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) + } else { + XCTFail() + } } func testHighlightingPerformance() { let input = fixture("swifttest.swift", "txt") self.measure { - _ = self.parser.attributedString(for: input) + _ = self.parser?.attributedString(for: input) } } } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 102375d..1abf70c 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -7,10 +7,10 @@ // import Foundation -import XCTest @testable import SyntaxKit +import XCTest -func fixture(_ name: String, _ type: String) -> String { +internal func fixture(_ name: String, _ type: String) -> String { if let path = Bundle(for: LanguageTests.self).path(forResource: name, ofType: type) { do { return try String(contentsOfFile: path) @@ -21,15 +21,15 @@ func fixture(_ name: String, _ type: String) -> String { return "" } -func getBundleManager() -> BundleManager { +internal func getBundleManager() -> BundleManager { return BundleManager { identifier, kind in - let name = kind == .Language ? identifier._split(separator: ".")[1] : identifier - let ext = kind == .Language ? ".tmLanguage" : ".tmTheme" + let name = kind == .language ? identifier._split(separator: ".")[1] : identifier + let ext = kind == .language ? ".tmLanguage" : ".tmTheme" return Bundle(for: LanguageTests.self).url(forResource: name.capitalized, withExtension: ext) ?? URL(fileURLWithPath: "") } } -func simpleTheme() -> Theme { +internal func simpleTheme() -> Theme? { return Theme(dictionary: [ "uuid": "123e4567-e89b-12d3-a456-426655440000", "name": "Simple", @@ -53,53 +53,24 @@ func simpleTheme() -> Theme { ] ] ] - ])! + ]) } -func replace(_ range: NSRange, in string: String, with inserted: String) -> String { +internal func replace(_ range: NSRange, in string: String, with inserted: String) -> String { let newInput = string.mutableCopy() as? NSMutableString newInput?.replaceCharacters(in: range, with: inserted) - return newInput!.copy() as? String ?? "" -} - -#if !os(OSX) -import UIKit -extension Color { - var redComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(&value, green: nil, blue: nil, alpha: nil) - return value - } - - var greenComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: &value, blue: nil, alpha: nil) - return value - } - - var blueComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: nil, blue: &value, alpha: nil) - return value - } - - var alphaComponent: CGFloat { - var value: CGFloat = 0.0 - getRed(nil, green: nil, blue: nil, alpha: &value) - return value - } + return newInput?.copy() as? String ?? "" } -#endif -func assertEqualColors(_ color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.005) { - if color1 == nil || color2 == nil { +internal func assertEqualColors(_ color1: Color?, _ color2: Color?, accuracy: CGFloat = 0.005) { + guard let lhColor = color1, let rhColor = color2 else { XCTAssert(false, "colors have to be non-nil") return } - XCTAssertEqualWithAccuracy(color1!.redComponent, color2!.redComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.greenComponent, color2!.greenComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.blueComponent, color2!.blueComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(color1!.alphaComponent, color2!.alphaComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(lhColor.redComponent, rhColor.redComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(lhColor.greenComponent, rhColor.greenComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(lhColor.blueComponent, rhColor.blueComponent, accuracy: accuracy) + XCTAssertEqualWithAccuracy(lhColor.alphaComponent, rhColor.alphaComponent, accuracy: accuracy) } extension NSRange: Equatable {} diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 0d106fa..4f0d854 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -6,36 +6,36 @@ // Copyright © 2015 Sam Soffes. All rights reserved. // -import XCTest @testable import SyntaxKit +import XCTest -class ThemeTests: XCTestCase { +internal class ThemeTests: XCTestCase { // MARK: - Properties - let manager = getBundleManager() - var tomorrow: Theme! - var solarized: Theme! + fileprivate let manager: BundleManager = getBundleManager() // MARK: - Tests - override func setUp() { - super.setUp() - tomorrow = manager.theme(withIdentifier: "Tomorrow") - solarized = manager.theme(withIdentifier: "Solarized") - } - func testLoading() { - XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) - XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) + if let tomorrow = manager.theme(withIdentifier: "Tomorrow") { + XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) + XCTAssertEqual("Tomorrow", tomorrow.name) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) + } else { + XCTFail() + } } func testComplexTheme() { - XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) - XCTAssertEqual("Solarized (light)", solarized.name) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as? Color) + if let solarized = manager.theme(withIdentifier: "Solarized") { + XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) + XCTAssertEqual("Solarized (light)", solarized.name) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as? Color) + } else { + XCTFail() + } } } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 84bfa4d..8c6c401 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -9,10 +9,10 @@ // Copyright © 2014-2015 Sam Soffes. All rights reserved. // -#if !os(OSX) - import UIKit -#else +#if os(OSX) import AppKit +#else + import UIKit #endif public typealias Attributes = [String: AnyObject] From 37b043ef26e9beb7f5f771eac6d9850fe24573c4 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Tue, 6 Jun 2017 14:05:57 +0200 Subject: [PATCH 098/110] fix all linter warnings! --- SyntaxKit/Parser.swift | 30 +++++++++--------------------- SyntaxKit/Pattern.swift | 31 +++---------------------------- SyntaxKit/Theme.swift | 2 -- 3 files changed, 12 insertions(+), 51 deletions(-) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 9ee462f..2581655 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -145,29 +145,17 @@ open class Parser { } } - if let middleMatch = bestMatchForMiddle { - let resultForMiddle: ResultSet? - if middleMatch.pattern.match != nil { - resultForMiddle = middleMatch.match - } else { - resultForMiddle = matchAfterBegin(of: middleMatch.pattern, beginResults: middleMatch.match) - } - - if let middleResult = resultForMiddle { - if middleResult.range.length != 0 { - result.add(middleResult) - let newStart = NSMaxRange(middleResult.range) - range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) - lineEnd = max(lineEnd, newStart) - } else { - break - } - } else { - break - } - } else { + guard let middleMatch = bestMatchForMiddle, + let middleResult = middleMatch.pattern.match != nil ? middleMatch.match : matchAfterBegin(of: middleMatch.pattern, beginResults: middleMatch.match) else { + break + } + if middleResult.range.length == 0 { break } + result.add(middleResult) + let newStart = NSMaxRange(middleResult.range) + range = NSRange(location: newStart, length: max(0, range.length - (newStart - range.location))) + lineEnd = max(lineEnd, newStart) } lineStart = lineEnd diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index ca1aa0f..2f09b11 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -45,8 +45,6 @@ internal class Pattern: NSObject { fileprivate var _applyEndPatternLast: Bool = false fileprivate weak var _parent: Pattern? - fileprivate let debug: Bool = true - // MARK: - Initializers init?(dictionary: [AnyHashable: Any], parent: Pattern?, with repository: Repository?, with manager: ReferenceManager) { @@ -56,23 +54,14 @@ internal class Pattern: NSObject { if let matchExpr = dictionary["match"] as? String { _match = try? NSRegularExpression(pattern: matchExpr, options: [.anchorsMatchLines]) - if debug && self.match == nil { - print("Problem parsing match expression \(matchExpr)") - } } if let beginExpr = dictionary["begin"] as? String { _begin = try? NSRegularExpression(pattern: beginExpr, options: [.anchorsMatchLines]) - if debug && self.begin == nil { - print("Problem parsing begin expression \(beginExpr)") - } } if let endExpr = dictionary["end"] as? String { _end = try? NSRegularExpression(pattern: endExpr, options: [.anchorsMatchLines]) - if debug && self.end == nil { - print("Problem parsing end expression \(endExpr)") - } } _applyEndPatternLast = dictionary["applyEndPatternLast"] as? Bool ?? false @@ -94,26 +83,12 @@ internal class Pattern: NSObject { _endCaptures = CaptureCollection(dictionary: dictionary) } - if dictionary["match"] as? String != nil && self.match == nil { - return nil - } else if dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) { + if dictionary["match"] as? String != nil && self.match == nil || + dictionary["begin"] as? String != nil && (self.begin == nil || self.end == nil) || + self.match == nil && self.begin == nil && self.end == nil && ((dictionary["patterns"] as? [[AnyHashable: Any]])?.isEmpty ?? true) { return nil } - if self.match == nil && - self.begin == nil && - self.end == nil { - if let patterns = dictionary["patterns"] as? [[AnyHashable: Any]] { - if patterns.isEmpty { - print("Attention: pattern not recognized: \(String(describing: self.name))") - return nil - } - } else { - print("Attention: pattern not recognized: \(String(describing: self.name))") - return nil - } - } - if let array = dictionary["patterns"] as? [[AnyHashable: Any]] { self.subpatterns = manager.patterns(for: array, in: repository, caller: self) } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 8c6c401..50ab95c 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -57,8 +57,6 @@ public struct Theme { setting[NSBackgroundColorAttributeName] = Color(hex: value) } - // TODO: caret, invisibles, lightHighlight, selection, font style - if let patternIdentifiers = raw["scope"] as? String { for patternIdentifier in patternIdentifiers.components(separatedBy: ",") { let key = patternIdentifier.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) From 144b22dd78166df8644d12ebee6648b0932c731e Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Mon, 19 Jun 2017 10:10:37 +0200 Subject: [PATCH 099/110] fix linter errors in latest swiftlint --- .swiftlint.yml | 3 +++ SyntaxKit/Color.swift | 4 ++-- SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift | 2 +- SyntaxKit/Tests/ThemeTests.swift | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 64b491c..a378dd6 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -10,6 +10,7 @@ opt_in_rules: - explicit_init - explicit_top_level_acl - explicit_type_interface + - extension_access_modifier - fatal_error_message - file_header - first_where @@ -17,6 +18,7 @@ opt_in_rules: - implicit_return - implicitly_unwrapped_optional - missing_docs + - multiline_parameters - nimble_operator - no_extension_access_modifier - number_separator @@ -29,6 +31,7 @@ opt_in_rules: - redundant_nil_coalesing - sorted_imports - switch_case_on_newline + - vertical_parameter_alignment_on_call file_header: required_pattern: | diff --git a/SyntaxKit/Color.swift b/SyntaxKit/Color.swift index fc36c9c..36326f4 100644 --- a/SyntaxKit/Color.swift +++ b/SyntaxKit/Color.swift @@ -11,7 +11,7 @@ public typealias ColorType = NSColor extension NSColor { - public convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { + convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { self.init(srgbRed: red, green: green, blue: blue, alpha: alpha) } } @@ -49,7 +49,7 @@ public typealias Color = ColorType extension Color { - public convenience init?(hex representation: String) { + convenience init?(hex representation: String) { var hex = representation as NSString // Remove `#` and `0x` diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index afc17b4..bff3779 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -7,7 +7,7 @@ // import Foundation -import SyntaxKit +@testable import SyntaxKit import XCTest internal class SwiftBaselineHighlightingTests: XCTestCase { diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 4f0d854..7c48460 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -21,8 +21,8 @@ internal class ThemeTests: XCTestCase { if let tomorrow = manager.theme(withIdentifier: "Tomorrow") { XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]![NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]?[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]?[NSForegroundColorAttributeName] as? Color) } else { XCTFail() } @@ -32,8 +32,8 @@ internal class ThemeTests: XCTestCase { if let solarized = manager.theme(withIdentifier: "Solarized") { XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) XCTAssertEqual("Solarized (light)", solarized.name) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]![NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]![NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]?[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]?[NSForegroundColorAttributeName] as? Color) } else { XCTFail() } From cef7722ad4358fe5a19340efa7cbd3f0bc4613aa Mon Sep 17 00:00:00 2001 From: Florian Ernst Date: Mon, 21 Aug 2017 01:37:52 +0200 Subject: [PATCH 100/110] Swift 4 fixes. --- SyntaxKit/AttributedParser.swift | 2 +- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Theme.swift | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index bdec3cc..726ce77 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -44,7 +44,7 @@ open class AttributedParser: Parser { } open func attributedString(for string: String, base: Attributes? = nil) -> NSAttributedString { - let output = NSMutableAttributedString(string: string, attributes: base) + let output = NSMutableAttributedString(string: string, attributes: base) output.beginEditing() parse(string) { _, range, attributes in if let attributes = attributes { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 2581655..ccc6536 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -274,7 +274,7 @@ open class Parser { print("Attention unexpected capture (\(index) to \(result.numberOfRanges)): \(expression.pattern)") continue } - let range = result.rangeAt(Int(index)) + let range = result.range(at: Int(index)) if range.location == NSNotFound { continue } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 50ab95c..41cd5fd 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -15,7 +15,7 @@ import UIKit #endif -public typealias Attributes = [String: AnyObject] +public typealias Attributes = [NSAttributedStringKey: Any] public struct Theme { @@ -26,11 +26,11 @@ public struct Theme { public let attributes: [String: Attributes] public var backgroundColor: Color { - return attributes[Language.globalScope]?[NSBackgroundColorAttributeName] as? Color ?? Color.white + return attributes[Language.globalScope]?[NSAttributedStringKey.backgroundColor] as? Color ?? Color.white } public var foregroundColor: Color { - return attributes[Language.globalScope]?[NSForegroundColorAttributeName] as? Color ?? Color.black + return attributes[Language.globalScope]?[NSAttributedStringKey.foregroundColor] as? Color ?? Color.black } // MARK: - Initializers @@ -47,14 +47,14 @@ public struct Theme { var attributes = [String: Attributes]() for raw in rawSettings { - guard var setting = raw["settings"] as? [String: AnyObject] else { continue } + guard var setting = raw["settings"] as? [NSAttributedStringKey: Any] else { continue } - if let value = setting.removeValue(forKey: "foreground") as? String { - setting[NSForegroundColorAttributeName] = Color(hex: value) + if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "foreground")) as? String { + setting[NSAttributedStringKey.foregroundColor] = Color(hex: value) } - if let value = setting.removeValue(forKey: "background") as? String { - setting[NSBackgroundColorAttributeName] = Color(hex: value) + if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "background")) as? String { + setting[NSAttributedStringKey.backgroundColor] = Color(hex: value) } if let patternIdentifiers = raw["scope"] as? String { From 60128e18355b0d8d8c3c03756ce89172074bad8d Mon Sep 17 00:00:00 2001 From: Florian Ernst Date: Mon, 21 Aug 2017 10:31:33 +0200 Subject: [PATCH 101/110] Fixed indentation. --- SyntaxKit/AttributedParser.swift | 2 +- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Theme.swift | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index 726ce77..bdec3cc 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -44,7 +44,7 @@ open class AttributedParser: Parser { } open func attributedString(for string: String, base: Attributes? = nil) -> NSAttributedString { - let output = NSMutableAttributedString(string: string, attributes: base) + let output = NSMutableAttributedString(string: string, attributes: base) output.beginEditing() parse(string) { _, range, attributes in if let attributes = attributes { diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index ccc6536..16cdd5f 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -274,7 +274,7 @@ open class Parser { print("Attention unexpected capture (\(index) to \(result.numberOfRanges)): \(expression.pattern)") continue } - let range = result.range(at: Int(index)) + let range = result.range(at: Int(index)) if range.location == NSNotFound { continue } diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index 41cd5fd..f458d4d 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -26,11 +26,11 @@ public struct Theme { public let attributes: [String: Attributes] public var backgroundColor: Color { - return attributes[Language.globalScope]?[NSAttributedStringKey.backgroundColor] as? Color ?? Color.white + return attributes[Language.globalScope]?[NSAttributedStringKey.backgroundColor] as? Color ?? Color.white } public var foregroundColor: Color { - return attributes[Language.globalScope]?[NSAttributedStringKey.foregroundColor] as? Color ?? Color.black + return attributes[Language.globalScope]?[NSAttributedStringKey.foregroundColor] as? Color ?? Color.black } // MARK: - Initializers @@ -49,11 +49,11 @@ public struct Theme { for raw in rawSettings { guard var setting = raw["settings"] as? [NSAttributedStringKey: Any] else { continue } - if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "foreground")) as? String { + if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "foreground")) as? String { setting[NSAttributedStringKey.foregroundColor] = Color(hex: value) } - if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "background")) as? String { + if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "background")) as? String { setting[NSAttributedStringKey.backgroundColor] = Color(hex: value) } From 44fe7c4d69205d96f007e35d09c1294d518e683b Mon Sep 17 00:00:00 2001 From: Florian Ernst Date: Mon, 21 Aug 2017 10:57:51 +0200 Subject: [PATCH 102/110] Fixed indentation. --- SyntaxKit/Theme.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SyntaxKit/Theme.swift b/SyntaxKit/Theme.swift index f458d4d..f551eb6 100644 --- a/SyntaxKit/Theme.swift +++ b/SyntaxKit/Theme.swift @@ -50,11 +50,11 @@ public struct Theme { guard var setting = raw["settings"] as? [NSAttributedStringKey: Any] else { continue } if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "foreground")) as? String { - setting[NSAttributedStringKey.foregroundColor] = Color(hex: value) + setting[NSAttributedStringKey.foregroundColor] = Color(hex: value) } if let value = setting.removeValue(forKey: NSAttributedStringKey(rawValue: "background")) as? String { - setting[NSAttributedStringKey.backgroundColor] = Color(hex: value) + setting[NSAttributedStringKey.backgroundColor] = Color(hex: value) } if let patternIdentifiers = raw["scope"] as? String { From 82fbd917a19c81ed4c48b65105e9457f5a74299a Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Sep 2017 15:37:56 +0200 Subject: [PATCH 103/110] update recommended project settings This also uses all the latest swiftlint rules. --- .swiftlint.yml | 12 ++++- SyntaxKit.xcodeproj/project.pbxproj | 46 ++++++++++++------- .../xcschemes/SyntaxKit-iOS.xcscheme | 4 +- .../xcschemes/SyntaxKit-macOS.xcscheme | 4 +- .../xcschemes/SyntaxKit-tvOS.xcscheme | 4 +- .../xcschemes/SyntaxKit-watchOS.xcscheme | 4 +- SyntaxKit/AttributedParser.swift | 2 +- SyntaxKit/AttributedParsingOperation.swift | 6 +-- SyntaxKit/BundleManager.swift | 8 ++-- SyntaxKit/CaptureCollection.swift | 2 +- SyntaxKit/Parser.swift | 12 ++--- SyntaxKit/Pattern.swift | 17 ++++--- SyntaxKit/ReferenceManager.swift | 4 +- SyntaxKit/Repository.swift | 4 +- SyntaxKit/Result.swift | 2 +- SyntaxKit/ResultSet.swift | 4 +- SyntaxKit/ScopedString.swift | 6 +-- SyntaxKit/Tests/AttributedParserTests.swift | 6 +-- SyntaxKit/Tests/IncrementalParsingTests.swift | 20 ++++---- SyntaxKit/Tests/LanguageTests.swift | 6 +-- SyntaxKit/Tests/ParserTests.swift | 34 +++++++------- SyntaxKit/Tests/PerformanceTests.swift | 6 +-- .../SwiftBaselineHighlightingTests.swift | 18 ++++---- SyntaxKit/Tests/TestHelper.swift | 16 ++----- SyntaxKit/Tests/ThemeTests.swift | 14 +++--- 25 files changed, 143 insertions(+), 118 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index a378dd6..cff2ddd 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,6 +7,7 @@ opt_in_rules: - closure_spacing - conditional_returns_on_newline - empty_count + - explicit_enum_raw_value - explicit_init - explicit_top_level_acl - explicit_type_interface @@ -17,20 +18,27 @@ opt_in_rules: - force_unwrapping - implicit_return - implicitly_unwrapped_optional - - missing_docs + - joined_default_parameter + - let_var_whitespace - multiline_parameters - nimble_operator - no_extension_access_modifier + - no_grouping_extension - number_separator - object_literal - operator_usage_whitespace - overridden_super_call + - pattern_matching_keywords - private_outlet - prohibited_super_call + - quick_discouraged_call - redundant_nil_coalescing - - redundant_nil_coalesing + - single_test_class - sorted_imports + - strict_fileprivate - switch_case_on_newline + - trailing_closure + - unneeded_parentheses_in_closure_argument - vertical_parameter_alignment_on_call file_header: diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 2e569da..828b829 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -566,7 +566,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Sam Soffes"; TargetAttributes = { 211826D91D257A71003F2BF2 = { @@ -890,7 +890,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; }; @@ -913,7 +913,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -931,7 +931,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit.tvos.tests; PRODUCT_NAME = SyntaxKitTests; SDKROOT = appletvos; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.2; }; name = Debug; @@ -947,7 +947,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit.tvos.tests; PRODUCT_NAME = SyntaxKitTests; SDKROOT = appletvos; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TVOS_DEPLOYMENT_TARGET = 9.2; VALIDATE_PRODUCT = YES; }; @@ -971,7 +971,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -994,7 +994,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1012,7 +1012,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1028,7 +1028,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -1042,14 +1042,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1080,7 +1086,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1095,14 +1101,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1126,7 +1138,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1152,7 +1164,7 @@ PRODUCT_NAME = SyntaxKit; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1175,7 +1187,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.samsoffes.syntaxkit; PRODUCT_NAME = SyntaxKit; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1197,7 +1209,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-macos.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1215,7 +1227,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-macos.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1236,7 +1248,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1259,7 +1271,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; WATCHOS_DEPLOYMENT_TARGET = 2.0; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index b536613..fc7cd69 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ @@ -56,6 +57,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme index e505f75..5d62a75 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/SyntaxKit/AttributedParser.swift b/SyntaxKit/AttributedParser.swift index bdec3cc..f6f1f3c 100644 --- a/SyntaxKit/AttributedParser.swift +++ b/SyntaxKit/AttributedParser.swift @@ -57,7 +57,7 @@ open class AttributedParser: Parser { // MARK: - Private - fileprivate func attributes(forScope scope: String) -> Attributes? { + private func attributes(forScope scope: String) -> Attributes? { let components = scope.components(separatedBy: ".") if components.isEmpty { return nil diff --git a/SyntaxKit/AttributedParsingOperation.swift b/SyntaxKit/AttributedParsingOperation.swift index c8c2769..1350d8e 100644 --- a/SyntaxKit/AttributedParsingOperation.swift +++ b/SyntaxKit/AttributedParsingOperation.swift @@ -64,9 +64,9 @@ open class AttributedParsingOperation: Operation { // MARK: - Properties - fileprivate let parser: AttributedParser - fileprivate let operationCallback: OperationCallback - fileprivate var parsedRange: NSRange? + private let parser: AttributedParser + private let operationCallback: OperationCallback + private var parsedRange: NSRange? // MARK: - Initializers diff --git a/SyntaxKit/BundleManager.swift b/SyntaxKit/BundleManager.swift index 398982b..a6a3760 100644 --- a/SyntaxKit/BundleManager.swift +++ b/SyntaxKit/BundleManager.swift @@ -39,10 +39,10 @@ open class BundleManager { open static var defaultManager: BundleManager? - fileprivate var bundleCallback: BundleLocationCallback - fileprivate var dependencies: [Language] = [] - fileprivate var cachedLanguages: [String: Language] = [:] - fileprivate var cachedThemes: [String: Theme] = [:] + private var bundleCallback: BundleLocationCallback + private var dependencies: [Language] = [] + private var cachedLanguages: [String: Language] = [:] + private var cachedThemes: [String: Theme] = [:] // MARK: - Initializers diff --git a/SyntaxKit/CaptureCollection.swift b/SyntaxKit/CaptureCollection.swift index a59b7b5..0aedf7d 100644 --- a/SyntaxKit/CaptureCollection.swift +++ b/SyntaxKit/CaptureCollection.swift @@ -12,7 +12,7 @@ internal struct CaptureCollection { // MARK: - Properties - fileprivate let captures: [UInt: Capture] + private let captures: [UInt: Capture] var captureIndexes: [UInt] { var keys = Array(captures.keys) diff --git a/SyntaxKit/Parser.swift b/SyntaxKit/Parser.swift index 16cdd5f..3ebcdb5 100644 --- a/SyntaxKit/Parser.swift +++ b/SyntaxKit/Parser.swift @@ -115,7 +115,7 @@ open class Parser { /// /// - returns: The result set containing the lexical scope names with range /// information or nil if aborted. May exceed range. - fileprivate func matchSubpatterns(of pattern: Pattern, in range: NSRange) -> ResultSet? { + private func matchSubpatterns(of pattern: Pattern, in range: NSRange) -> ResultSet? { let stop = range.location + range.length var lineStart = range.location var lineEnd = range.location @@ -174,7 +174,7 @@ open class Parser { /// /// - returns: The matched pattern and the matching result. Nil on failure. /// The results range may exceed the passed in range. - fileprivate func match(_ patterns: [Pattern], in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { + private func match(_ patterns: [Pattern], in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { var interestingBounds = range var bestResult: (pattern: Pattern, match: ResultSet)? for pattern in patterns { @@ -202,7 +202,7 @@ open class Parser { /// - parameter range: The range in which to match the pattern /// /// - returns: The matched pattern and the matching result. Nil on failure. - fileprivate func firstMatch(of pattern: Pattern, in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { + private func firstMatch(of pattern: Pattern, in range: NSRange) -> (pattern: Pattern, match: ResultSet)? { if let expression = pattern.match { if let resultSet = match(expression, in: range, captures: pattern.captures, baseSelector: pattern.name) { if resultSet.range.length != 0 { @@ -229,7 +229,7 @@ open class Parser { /// has to be matched /// - parameter begin: The match result of the beginning /// - returns: The result of matching the given pattern or nil on abortion. - fileprivate func matchAfterBegin(of pattern: Pattern, beginResults begin: ResultSet) -> ResultSet? { + private func matchAfterBegin(of pattern: Pattern, beginResults begin: ResultSet) -> ResultSet? { let newLocation = NSMaxRange(begin.range) guard let endResults = matchSubpatterns(of: pattern, in: NSRange(location: newLocation, length: (toParse.string as NSString).length - newLocation)) else { return nil @@ -258,7 +258,7 @@ open class Parser { /// - returns: The set containing the results. May be nil if the expression /// could not match any part of the string. It may also be empty /// and only contain range information to show what it matched. - fileprivate func match(_ expression: NSRegularExpression, in range: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { + private func match(_ expression: NSRegularExpression, in range: NSRange, captures: CaptureCollection?, baseSelector: String? = nil) -> ResultSet? { guard let result = expression.firstMatch(in: toParse.string, options: [.withTransparentBounds], range: range) else { return nil } @@ -293,7 +293,7 @@ open class Parser { /// /// - parameter results: The results of the parsing pass /// - parameter callback: The method to call on every successful match - fileprivate func apply(_ results: ResultSet, callback: Callback) { + private func apply(_ results: ResultSet, callback: Callback) { callback(Language.globalScope, results.range) for result in results.results where result.range.length > 0 { if result.attribute != nil { diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index 2f09b11..b05007c 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -35,6 +35,7 @@ internal class Pattern: NSObject { var parent: Pattern? { return _parent } var subpatterns: [Pattern] = [] + // swiftlint:disable strict_fileprivate fileprivate var _name: String? fileprivate var _match: NSRegularExpression? fileprivate var _captures: CaptureCollection? @@ -44,6 +45,7 @@ internal class Pattern: NSObject { fileprivate var _endCaptures: CaptureCollection? fileprivate var _applyEndPatternLast: Bool = false fileprivate weak var _parent: Pattern? + // swiftlint:enable strict_fileprivate // MARK: - Initializers @@ -126,23 +128,23 @@ internal class Include: Pattern { case resolved } - fileprivate var type: ReferenceType - fileprivate var associatedRepository: Repository? + private var type: ReferenceType + private var associatedRepository: Repository? // MARK: - Initializers init(reference: String, in repository: Repository? = nil, parent: Pattern?, manager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { - self.type = .toRepository(repositoryRef: reference.substring(from: reference.characters.index(after: reference.startIndex))) + self.type = .toRepository(repositoryRef: String(reference[reference.characters.index(after: reference.startIndex)...])) } else if reference == "$self" { self.type = .toSelf } else if reference == "$base" { self.type = .toBase } else if reference.contains("#") { if let hashRange = reference.range(of: "#") { - let languagePart = reference.substring(to: hashRange.lowerBound) - self.type = .toForeignRepository(repositoryRef: reference.substring(from: hashRange.upperBound), languageRef: languagePart) + let languagePart = String(reference[.. Bool { return lhs.patternIdentifier == rhs.patternIdentifier && - lhs.range.toRange() == rhs.range.toRange() + Range(lhs.range) == Range(rhs.range) } diff --git a/SyntaxKit/ResultSet.swift b/SyntaxKit/ResultSet.swift index c9d7af3..0f379b3 100644 --- a/SyntaxKit/ResultSet.swift +++ b/SyntaxKit/ResultSet.swift @@ -17,8 +17,8 @@ internal class ResultSet { /// associated with the contained results. var range: NSRange { return _range } - fileprivate var _results: [Result] = [] - fileprivate var _range: NSRange + private var _results: [Result] = [] + private var _range: NSRange // MARK: - Initializers diff --git a/SyntaxKit/ScopedString.swift b/SyntaxKit/ScopedString.swift index fc0e1f9..5d6f612 100644 --- a/SyntaxKit/ScopedString.swift +++ b/SyntaxKit/ScopedString.swift @@ -77,7 +77,7 @@ internal struct ScopedString { var string: String - fileprivate var levels: [[Scope]] = [] + private var levels: [[Scope]] = [] /// The inplicit scope at the base of each ScopedString var baseScope: Scope { @@ -279,7 +279,7 @@ internal struct ScopedString { // MARK: - Private - fileprivate func findScopeIntersection(with range: NSRange, at level: [Scope]) -> Scope? { + private func findScopeIntersection(with range: NSRange, at level: [Scope]) -> Scope? { for scope in level { if scope.range.partiallyContains(range) { return scope @@ -288,7 +288,7 @@ internal struct ScopedString { return nil } - fileprivate func insertionPoint(for range: NSRange, at level: [Scope]) -> Int { + private func insertionPoint(for range: NSRange, at level: [Scope]) -> Int { var i = 0 for scope in level { if range.location < scope.range.location { diff --git a/SyntaxKit/Tests/AttributedParserTests.swift b/SyntaxKit/Tests/AttributedParserTests.swift index c20dcde..e4b2754 100644 --- a/SyntaxKit/Tests/AttributedParserTests.swift +++ b/SyntaxKit/Tests/AttributedParserTests.swift @@ -13,8 +13,8 @@ internal class AttributedParserTests: XCTestCase { // MARK: - Properties - fileprivate let manager: BundleManager = getBundleManager() - fileprivate var parser: AttributedParser? + private let manager: BundleManager = getBundleManager() + private var parser: AttributedParser? // MARK: - Tests @@ -24,7 +24,7 @@ internal class AttributedParserTests: XCTestCase { let theme = simpleTheme() { parser = AttributedParser(language: yaml, theme: theme) } else { - XCTFail() + XCTFail("Should be able to load yaml") } } diff --git a/SyntaxKit/Tests/IncrementalParsingTests.swift b/SyntaxKit/Tests/IncrementalParsingTests.swift index a230376..29028fb 100644 --- a/SyntaxKit/Tests/IncrementalParsingTests.swift +++ b/SyntaxKit/Tests/IncrementalParsingTests.swift @@ -13,10 +13,10 @@ internal class IncrementalParsingTests: XCTestCase { // MARK: - Properties - fileprivate let manager: BundleManager = getBundleManager() - fileprivate var parsingOperation: AttributedParsingOperation? - fileprivate var totalRange: NSRange? - fileprivate var input: String = "" + private let manager: BundleManager = getBundleManager() + private var parsingOperation: AttributedParsingOperation? + private var totalRange: NSRange? + private var input: String = "" // MARK: - Tests @@ -89,7 +89,7 @@ internal class IncrementalParsingTests: XCTestCase { // MARK: - Helpers - fileprivate func getParsingOperation() -> AttributedParsingOperation? { + private func getParsingOperation() -> AttributedParsingOperation? { if let language = manager.language(withIdentifier: "Source.swift"), let theme = manager.theme(withIdentifier: "tomorrow") { return AttributedParsingOperation(string: input, language: language, theme: theme) { (results: [(range: NSRange, attributes: Attributes?)], _: AttributedParsingOperation) in @@ -102,12 +102,12 @@ internal class IncrementalParsingTests: XCTestCase { } } } else { - XCTFail() + XCTFail("Should be able to load swift language fixture") return nil } } - fileprivate func assertInsertion(_ string: String, location: Int, expectedRange expected: NSRange) { + private func assertInsertion(_ string: String, location: Int, expectedRange expected: NSRange) { input = replace(NSRange(location: location, length: 0), in: input, with: string) if let previousOperation = parsingOperation { parsingOperation = AttributedParsingOperation(string: input, previousOperation: previousOperation, changeIsInsertion: true, changedRange: NSRange(location: location, length: (string as NSString).length)) @@ -116,11 +116,11 @@ internal class IncrementalParsingTests: XCTestCase { parsingOperation?.main() XCTAssertEqual(totalRange, expected) } else { - XCTFail() + XCTFail("Should have been able to get parsing operation") } } - fileprivate func assertDeletion(_ range: NSRange, expectedRange expected: NSRange) { + private func assertDeletion(_ range: NSRange, expectedRange expected: NSRange) { input = replace(range, in: input, with: "") if let previousOperation = parsingOperation { parsingOperation = AttributedParsingOperation(string: input, previousOperation: previousOperation, changeIsInsertion: false, changedRange: range) @@ -129,7 +129,7 @@ internal class IncrementalParsingTests: XCTestCase { parsingOperation?.main() XCTAssertEqual(totalRange, expected) } else { - XCTFail() + XCTFail("Should have been able to get parsing operation") } } } diff --git a/SyntaxKit/Tests/LanguageTests.swift b/SyntaxKit/Tests/LanguageTests.swift index 88be620..69e592b 100644 --- a/SyntaxKit/Tests/LanguageTests.swift +++ b/SyntaxKit/Tests/LanguageTests.swift @@ -13,7 +13,7 @@ internal class LanguageTests: XCTestCase { // MARK: - Properties - fileprivate let manager: BundleManager = getBundleManager() + private let manager: BundleManager = getBundleManager() // MARK: - Tests @@ -36,7 +36,7 @@ internal class LanguageTests: XCTestCase { XCTAssertEqual("string.unquoted.yaml", pattern.name) XCTAssertEqual("punctuation.definition.entry.yaml", pattern.captures?[1]?.name) } else { - XCTFail() + XCTFail("Should be able to load yaml language fixture") } } @@ -50,7 +50,7 @@ internal class LanguageTests: XCTestCase { XCTAssertEqual(4, swift.pattern.subpatterns[1].subpatterns.count) XCTAssertEqual("comment.line.double-slash.swift", swift.pattern.subpatterns[1].subpatterns[3].subpatterns[0].name) } else { - XCTFail() + XCTFail("Should be able to load swift language fixture") } } } diff --git a/SyntaxKit/Tests/ParserTests.swift b/SyntaxKit/Tests/ParserTests.swift index 3a5b30b..efe73c2 100644 --- a/SyntaxKit/Tests/ParserTests.swift +++ b/SyntaxKit/Tests/ParserTests.swift @@ -13,8 +13,8 @@ internal class ParserTests: XCTestCase { // MARK: - Properties - fileprivate var parser: Parser? - fileprivate let manager: BundleManager = getBundleManager() + private var parser: Parser? + private let manager: BundleManager = getBundleManager() // MARK: - Tests @@ -23,7 +23,7 @@ internal class ParserTests: XCTestCase { if let yaml = manager.language(withIdentifier: "source.YAML") { parser = Parser(language: yaml) } else { - XCTFail() + XCTFail("Should be able to load yaml language fixture") } } @@ -32,17 +32,17 @@ internal class ParserTests: XCTestCase { var punctuationBegin: NSRange? var punctuationEnd: NSRange? - parser?.parse("title: \"Hello World\"\n") { (result: (scope: String, range: NSRange)) in - if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { - stringQuoted = result.range + parser?.parse("title: \"Hello World\"\n") { (scope: String, range: NSRange) in + if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { + stringQuoted = range } - if punctuationBegin == nil && result.scope.hasPrefix("punctuation.definition.string.begin") { - punctuationBegin = result.range + if punctuationBegin == nil && scope.hasPrefix("punctuation.definition.string.begin") { + punctuationBegin = range } - if punctuationEnd == nil && result.scope.hasPrefix("punctuation.definition.string.end") { - punctuationEnd = result.range + if punctuationEnd == nil && scope.hasPrefix("punctuation.definition.string.end") { + punctuationEnd = range } } @@ -54,9 +54,9 @@ internal class ParserTests: XCTestCase { func testParsingBeginEndGarbage() { var stringQuoted: NSRange? - parser?.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (result: (scope: String, range: NSRange)) in - if stringQuoted == nil && result.scope.hasPrefix("string.quoted.double") { - stringQuoted = result.range + parser?.parse("title: Hello World\ncomments: 24\nposts: \"12\"zz\n") { (scope: String, range: NSRange) in + if stringQuoted == nil && scope.hasPrefix("string.quoted.double") { + stringQuoted = range } } @@ -64,17 +64,17 @@ internal class ParserTests: XCTestCase { } func testParsingGarbage() { - parser?.parse("") { _ in } - parser?.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _ in } + parser?.parse("") { _, _ in } + parser?.parse("ainod adlkf ac\nv a;skcja\nsd flaksdfj [awiefasdvxzc\\vzxcx c\n\n\nx \ncvas\ndv\nas \ndf as]pkdfa \nsd\nfa sdos[a \n\n a\ns cvsa\ncd\n a \ncd\n \n\n\n asdcp[vk sa\n\ndd'; \nssv[ das \n\n\nlkjs") { _, _ in } } func testRuby() { if let ruby = manager.language(withIdentifier: "source.Ruby") { parser = Parser(language: ruby) let input = fixture("test.rb", "txt") - parser?.parse(input, match: { _ in return }) + parser?.parse(input) { _, _ in return } } else { - XCTFail() + XCTFail("Should be able to load ruby language fixture") } } } diff --git a/SyntaxKit/Tests/PerformanceTests.swift b/SyntaxKit/Tests/PerformanceTests.swift index b08756a..17849c4 100644 --- a/SyntaxKit/Tests/PerformanceTests.swift +++ b/SyntaxKit/Tests/PerformanceTests.swift @@ -13,8 +13,8 @@ internal class PerformanceTests: XCTestCase { // MARK: - Properties - fileprivate let manager: BundleManager = getBundleManager() - fileprivate var parser: AttributedParser? + private let manager: BundleManager = getBundleManager() + private var parser: AttributedParser? override func setUp() { super.setUp() @@ -22,7 +22,7 @@ internal class PerformanceTests: XCTestCase { let solarized = manager.theme(withIdentifier: "Solarized") { parser = AttributedParser(language: latex, theme: solarized) } else { - XCTFail() + XCTFail("Should be able to load latex language fixture") } } diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index bff3779..9b827dc 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -25,7 +25,7 @@ internal class SwiftBaselineHighlightingTests: XCTestCase { let solarized = manager.theme(withIdentifier: "Solarized") { parser = AttributedParser(language: swift, theme: solarized) } else { - XCTFail() + XCTFail("Should be able to load swift language fixture") } } @@ -33,28 +33,28 @@ internal class SwiftBaselineHighlightingTests: XCTestCase { let input = fixture("swifttest.swift", "txt") if let string = parser?.attributedString(for: input) { // line comment - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) // block comment // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) // string literal // print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) var stringRange = NSRange() - assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSAttributedStringKey.foregroundColor] as? Color) XCTAssertEqual(stringRange.length, 4) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) // number literal var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) - assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSAttributedStringKey.foregroundColor] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } else { - XCTFail() + XCTFail("Parser loading should have succeeded") } } diff --git a/SyntaxKit/Tests/TestHelper.swift b/SyntaxKit/Tests/TestHelper.swift index 1abf70c..facf4d6 100644 --- a/SyntaxKit/Tests/TestHelper.swift +++ b/SyntaxKit/Tests/TestHelper.swift @@ -23,7 +23,7 @@ internal func fixture(_ name: String, _ type: String) -> String { internal func getBundleManager() -> BundleManager { return BundleManager { identifier, kind in - let name = kind == .language ? identifier._split(separator: ".")[1] : identifier + let name = kind == .language ? String(identifier.split(separator: ".")[1]) : identifier let ext = kind == .language ? ".tmLanguage" : ".tmTheme" return Bundle(for: LanguageTests.self).url(forResource: name.capitalized, withExtension: ext) ?? URL(fileURLWithPath: "") } @@ -67,14 +67,8 @@ internal func assertEqualColors(_ color1: Color?, _ color2: Color?, accuracy: CG XCTAssert(false, "colors have to be non-nil") return } - XCTAssertEqualWithAccuracy(lhColor.redComponent, rhColor.redComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(lhColor.greenComponent, rhColor.greenComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(lhColor.blueComponent, rhColor.blueComponent, accuracy: accuracy) - XCTAssertEqualWithAccuracy(lhColor.alphaComponent, rhColor.alphaComponent, accuracy: accuracy) -} - -extension NSRange: Equatable {} - -public func == (lhs: NSRange, rhs: NSRange) -> Bool { - return lhs.location == rhs.location && lhs.length == rhs.length + XCTAssertEqual(lhColor.redComponent, rhColor.redComponent, accuracy: accuracy) + XCTAssertEqual(lhColor.greenComponent, rhColor.greenComponent, accuracy: accuracy) + XCTAssertEqual(lhColor.blueComponent, rhColor.blueComponent, accuracy: accuracy) + XCTAssertEqual(lhColor.alphaComponent, rhColor.alphaComponent, accuracy: accuracy) } diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 7c48460..58ab102 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -13,7 +13,7 @@ internal class ThemeTests: XCTestCase { // MARK: - Properties - fileprivate let manager: BundleManager = getBundleManager() + private let manager: BundleManager = getBundleManager() // MARK: - Tests @@ -21,10 +21,10 @@ internal class ThemeTests: XCTestCase { if let tomorrow = manager.theme(withIdentifier: "Tomorrow") { XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]?[NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]?[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]?[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]?[NSAttributedStringKey.foregroundColor] as? Color) } else { - XCTFail() + XCTFail("Should be able to load tomorrow theme fixture") } } @@ -32,10 +32,10 @@ internal class ThemeTests: XCTestCase { if let solarized = manager.theme(withIdentifier: "Solarized") { XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) XCTAssertEqual("Solarized (light)", solarized.name) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]?[NSForegroundColorAttributeName] as? Color) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]?[NSForegroundColorAttributeName] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]?[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]?[NSAttributedStringKey.foregroundColor] as? Color) } else { - XCTFail() + XCTFail("Should be able to load solarized theme fixture") } } } From 083fd6bd48fa7cf63517c9d2991d4bfe6f685107 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 17 Sep 2017 15:43:21 +0200 Subject: [PATCH 104/110] update readme to say swift 4 --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index cc6f64d..9eba270 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -9,7 +9,7 @@ SyntaxKit was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building -SyntaxKit is written in Swift 3 so Xcode 8 is required. There aren't any dependencies besides system frameworks. +SyntaxKit is written in Swift 4 so Xcode 9 is required. There aren't any dependencies besides system frameworks. ## Installation From 647b11c579867e24f390e04c1fe01d4c520bc4f7 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 17 Aug 2018 15:36:19 +0200 Subject: [PATCH 105/110] Update to new recommended settings --- SyntaxKit.xcodeproj/project.pbxproj | 6 +++++- .../xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/SyntaxKit-macOS.xcscheme | 8 +++----- .../xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme | 4 +--- .../xcshareddata/xcschemes/SyntaxKit-watchOS.xcscheme | 4 +--- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 828b829..ecfaff7 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -566,7 +566,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "Sam Soffes"; TargetAttributes = { 211826D91D257A71003F2BF2 = { @@ -1046,12 +1046,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1105,12 +1107,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index fc7cd69..a50633d 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -57,7 +56,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme index 5d62a75..01dfff9 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme @@ -1,6 +1,6 @@ @@ -37,7 +36,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" From 09d7ffe5e4c4c5498885a295329b3f766b2b39b1 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 17 Aug 2018 18:51:12 +0200 Subject: [PATCH 106/110] Use swift 4 string improvements --- SyntaxKit/Pattern.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SyntaxKit/Pattern.swift b/SyntaxKit/Pattern.swift index b05007c..4e27605 100644 --- a/SyntaxKit/Pattern.swift +++ b/SyntaxKit/Pattern.swift @@ -136,7 +136,7 @@ internal class Include: Pattern { init(reference: String, in repository: Repository? = nil, parent: Pattern?, manager: BundleManager) { self.associatedRepository = repository if reference.hasPrefix("#") { - self.type = .toRepository(repositoryRef: String(reference[reference.characters.index(after: reference.startIndex)...])) + self.type = .toRepository(repositoryRef: String(reference[reference.index(after: reference.startIndex)...])) } else if reference == "$self" { self.type = .toSelf } else if reference == "$base" { From c7c0391006df8540a3035b8039553093277f17e3 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Fri, 14 Sep 2018 11:55:44 +0200 Subject: [PATCH 107/110] Update to Xcode 10 and Swift 4.2 --- SyntaxKit.xcodeproj/project.pbxproj | 6 +++--- .../xcschemes/SyntaxKit-iOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-macOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-tvOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-watchOS.xcscheme | 2 +- SyntaxKit/AttributedParser.swift | 2 +- SyntaxKit/BundleManager.swift | 2 +- SyntaxKit/Parser.swift | 2 +- SyntaxKit/Theme.swift | 16 ++++++++-------- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index ecfaff7..5ebc757 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -577,7 +577,7 @@ }; 211989A61B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 1000; }; 211989AF1B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; @@ -971,7 +971,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -994,7 +994,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index a50633d..6728f71 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ Date: Sun, 11 Aug 2019 19:51:07 +0200 Subject: [PATCH 108/110] Update to Swift 5 --- SyntaxKit.xcodeproj/project.pbxproj | 13 ++++++++----- .../xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SyntaxKit-macOS.xcscheme | 2 +- .../xcshareddata/xcschemes/SyntaxKit-tvOS.xcscheme | 2 +- .../xcschemes/SyntaxKit-watchOS.xcscheme | 2 +- SyntaxKit/BundleManager.swift | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index 5ebc757..ba27446 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -566,7 +566,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0940; + LastUpgradeCheck = 1030; ORGANIZATIONNAME = "Sam Soffes"; TargetAttributes = { 211826D91D257A71003F2BF2 = { @@ -577,7 +577,7 @@ }; 211989A61B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 1000; + LastSwiftMigration = 1030; }; 211989AF1B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; @@ -596,10 +596,11 @@ }; buildConfigurationList = 2122A6D81B22B9320006409B /* Build configuration list for PBXProject "SyntaxKit" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 2122A6D41B22B9320006409B; productRefGroup = 2122A6DF1B22B9320006409B /* Products */; @@ -971,7 +972,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -994,7 +995,7 @@ PRODUCT_NAME = SyntaxKit; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -1038,6 +1039,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -1099,6 +1101,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; diff --git a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme index 6728f71..36486f5 100644 --- a/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme +++ b/SyntaxKit.xcodeproj/xcshareddata/xcschemes/SyntaxKit-iOS.xcscheme @@ -1,6 +1,6 @@ Language? { - let indexOfStoredLanguage = self.dependencies.index { (lang: Language) in lang.scopeName == identifier } + let indexOfStoredLanguage = self.dependencies.firstIndex { (lang: Language) in lang.scopeName == identifier } if let index = indexOfStoredLanguage { return self.dependencies[index] From da57a63451304bb0f8f8e10d0e236cb76f99c8ad Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 11 Aug 2019 20:07:45 +0200 Subject: [PATCH 109/110] Update readme with swift 5 notice --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index 9eba270..b0b9580 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -9,7 +9,7 @@ SyntaxKit was originally abstracted from [Whiskey](http://usewhiskey.com). ## Building -SyntaxKit is written in Swift 4 so Xcode 9 is required. There aren't any dependencies besides system frameworks. +SyntaxKit is written in Swift 5 so Xcode 10 is required. There aren't any dependencies besides system frameworks. ## Installation From ea3dc4fe35578cc944551e988f68649bf809a2b2 Mon Sep 17 00:00:00 2001 From: Alexander Hedges Date: Sun, 11 Aug 2019 20:12:35 +0200 Subject: [PATCH 110/110] Update test target to swift 5 --- SyntaxKit.xcodeproj/project.pbxproj | 6 +++--- .../Tests/SwiftBaselineHighlightingTests.swift | 14 +++++++------- SyntaxKit/Tests/ThemeTests.swift | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/SyntaxKit.xcodeproj/project.pbxproj b/SyntaxKit.xcodeproj/project.pbxproj index ba27446..281827a 100644 --- a/SyntaxKit.xcodeproj/project.pbxproj +++ b/SyntaxKit.xcodeproj/project.pbxproj @@ -581,7 +581,7 @@ }; 211989AF1B2EC3B600F0D786 = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0800; + LastSwiftMigration = 1030; }; 2122A6DD1B22B9320006409B = { CreatedOnToolsVersion = 6.3.2; @@ -1013,7 +1013,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1029,7 +1029,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.samsoffes.syntaxkit-ios.tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift index 9b827dc..a5be91c 100644 --- a/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift +++ b/SyntaxKit/Tests/SwiftBaselineHighlightingTests.swift @@ -33,25 +33,25 @@ internal class SwiftBaselineHighlightingTests: XCTestCase { let input = fixture("swifttest.swift", "txt") if let string = parser?.attributedString(for: input) { // line comment - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 10, effectiveRange: nil)[NSAttributedString.Key.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 135, effectiveRange: nil)[NSAttributedString.Key.foregroundColor] as? Color) // block comment // print((string.string as NSString).substringWithRange(NSRange(location: 157, length: 20))) - assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#93A1A1"), string.attributes(at: 157, effectiveRange: nil)[NSAttributedString.Key.foregroundColor] as? Color) // string literal // print((string.string as NSString).substringWithRange(NSRange(location: 744, length: 6))) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 744, effectiveRange: nil)[NSAttributedString.Key.foregroundColor] as? Color) var stringRange = NSRange() - assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#2aa198"), string.attributes(at: 745, effectiveRange: &stringRange)[NSAttributedString.Key.foregroundColor] as? Color) XCTAssertEqual(stringRange.length, 4) - assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#839496"), string.attributes(at: 749, effectiveRange: nil)[NSAttributedString.Key.foregroundColor] as? Color) // number literal var numberRange = NSRange() // print((string.string as NSString).substringWithRange(NSRange(location: 715, length: 3))) - assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#d33682"), string.attributes(at: 715, effectiveRange: &numberRange)[NSAttributedString.Key.foregroundColor] as? Color) XCTAssertEqual(numberRange, NSRange(location: 715, length: 1)) } else { XCTFail("Parser loading should have succeeded") diff --git a/SyntaxKit/Tests/ThemeTests.swift b/SyntaxKit/Tests/ThemeTests.swift index 58ab102..5c7a41e 100644 --- a/SyntaxKit/Tests/ThemeTests.swift +++ b/SyntaxKit/Tests/ThemeTests.swift @@ -21,8 +21,8 @@ internal class ThemeTests: XCTestCase { if let tomorrow = manager.theme(withIdentifier: "Tomorrow") { XCTAssertEqual(UUID(uuidString: "82CCD69C-F1B1-4529-B39E-780F91F07604"), tomorrow.uuid) XCTAssertEqual("Tomorrow", tomorrow.name) - assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]?[NSAttributedStringKey.foregroundColor] as? Color) - assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]?[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#666969"), tomorrow.attributes["constant.other"]?[NSAttributedString.Key.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#4271AE"), tomorrow.attributes["keyword.other.special-method"]?[NSAttributedString.Key.foregroundColor] as? Color) } else { XCTFail("Should be able to load tomorrow theme fixture") } @@ -32,8 +32,8 @@ internal class ThemeTests: XCTestCase { if let solarized = manager.theme(withIdentifier: "Solarized") { XCTAssertEqual(UUID(uuidString: "38E819D9-AE02-452F-9231-ECC3B204AFD7"), solarized.uuid) XCTAssertEqual("Solarized (light)", solarized.name) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]?[NSAttributedStringKey.foregroundColor] as? Color) - assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]?[NSAttributedStringKey.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.double"]?[NSAttributedString.Key.foregroundColor] as? Color) + assertEqualColors(Color(hex: "#2aa198"), solarized.attributes["string.quoted.single"]?[NSAttributedString.Key.foregroundColor] as? Color) } else { XCTFail("Should be able to load solarized theme fixture") }