Move TextEditing extensions to swift package

This commit is contained in:
1024jp 2024-06-19 08:17:27 +09:00
parent e8be806353
commit a60366dca3
48 changed files with 1007 additions and 800 deletions

View File

@ -66,6 +66,9 @@
2A11F2141E669BFA005E1675 /* PointerBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A11F2121E669BFA005E1675 /* PointerBridge.swift */; };
2A1311D62127DCE1001D52C5 /* NSTextView+CurrentLineHighlighting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1311D52127DCE1001D52C5 /* NSTextView+CurrentLineHighlighting.swift */; };
2A1311D72127DCE1001D52C5 /* NSTextView+CurrentLineHighlighting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1311D52127DCE1001D52C5 /* NSTextView+CurrentLineHighlighting.swift */; };
2A1380072C225FAF00093BF3 /* NSTextStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1380062C225FAF00093BF3 /* NSTextStorageTests.swift */; };
2A13800C2C23076300093BF3 /* FileEncoding in Frameworks */ = {isa = PBXBuildFile; productRef = 2ADF96402C1B05CD00B6B722 /* FileEncoding */; };
2A13800D2C23076300093BF3 /* FileEncoding in Frameworks */ = {isa = PBXBuildFile; productRef = 2ADF96422C1B05D300B6B722 /* FileEncoding */; };
2A158C1C2945A6B1000A4EC1 /* HeadingMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A158C1B2945A6B1000A4EC1 /* HeadingMenuItem.swift */; };
2A158C1D2945A6B1000A4EC1 /* HeadingMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A158C1B2945A6B1000A4EC1 /* HeadingMenuItem.swift */; };
2A158C1F2945E423000A4EC1 /* SavePanelAccessory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A158C1E2945E423000A4EC1 /* SavePanelAccessory.swift */; };
@ -181,8 +184,6 @@
2A2B086028046E3B0028D733 /* WarningInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2B085F28046E3B0028D733 /* WarningInspectorView.swift */; };
2A2B086128046E3B0028D733 /* WarningInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2B085F28046E3B0028D733 /* WarningInspectorView.swift */; };
2A2E56D72C018ADB00416F9E /* ComparableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2E56D62C018ADB00416F9E /* ComparableTests.swift */; };
2A2E56DB2C057FBF00416F9E /* BracePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2E56DA2C057FBF00416F9E /* BracePair.swift */; };
2A2E56DC2C057FBF00416F9E /* BracePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2E56DA2C057FBF00416F9E /* BracePair.swift */; };
2A2EEF182B778BB1001FEDFB /* WrappingHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2EEF172B778BB1001FEDFB /* WrappingHStack.swift */; };
2A2EEF192B778BB1001FEDFB /* WrappingHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2EEF172B778BB1001FEDFB /* WrappingHStack.swift */; };
2A30C7DB2B1380BE002F6381 /* ShortcutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A30C7DA2B1380BE002F6381 /* ShortcutView.swift */; };
@ -391,8 +392,6 @@
2A6FD9E81D394F5900A59784 /* LayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9E61D394F5900A59784 /* LayoutManager.swift */; };
2A6FD9EA1D3A819500A59784 /* EditorTextView+Commenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9E91D3A819500A59784 /* EditorTextView+Commenting.swift */; };
2A6FD9EB1D3A819500A59784 /* EditorTextView+Commenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9E91D3A819500A59784 /* EditorTextView+Commenting.swift */; };
2A6FD9ED1D3A85D700A59784 /* NSString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9EC1D3A85D700A59784 /* NSString.swift */; };
2A6FD9EE1D3A85D700A59784 /* NSString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9EC1D3A85D700A59784 /* NSString.swift */; };
2A6FD9F61D3AE29E00A59784 /* Syntax.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9F51D3AE29E00A59784 /* Syntax.swift */; };
2A6FD9F71D3AE29E00A59784 /* Syntax.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6FD9F51D3AE29E00A59784 /* Syntax.swift */; };
2A719F6623CD92370026F877 /* FuzzyRangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A719F6523CD92370026F877 /* FuzzyRangeTests.swift */; };
@ -402,8 +401,6 @@
2A71BC7F1DDC70A80085AE1C /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A71BC7D1DDC70A80085AE1C /* NSImage.swift */; };
2A72DA10209B778B005242B9 /* NSTextView+MultiCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72DA0F209B778B005242B9 /* NSTextView+MultiCursor.swift */; };
2A72DA11209B778B005242B9 /* NSTextView+MultiCursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72DA0F209B778B005242B9 /* NSTextView+MultiCursor.swift */; };
2A733E8920BBB4AC0090D7CB /* String+Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A733E8820BBB4AC0090D7CB /* String+Case.swift */; };
2A733E8A20BBB4AC0090D7CB /* String+Case.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A733E8820BBB4AC0090D7CB /* String+Case.swift */; };
2A73B9332A8F6620002F3A16 /* RegexTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B9322A8F6620002F3A16 /* RegexTextField.swift */; };
2A73B9342A8F6620002F3A16 /* RegexTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B9322A8F6620002F3A16 /* RegexTextField.swift */; };
2A7470692B12FA5700669A7B /* NSTextStorage+TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A7470682B12FA5700669A7B /* NSTextStorage+TextView.swift */; };
@ -454,16 +451,12 @@
2A8E47E2299A2314006A40D8 /* EditedRangeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E1299A2314006A40D8 /* EditedRangeSet.swift */; };
2A8E47E3299A2314006A40D8 /* EditedRangeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E1299A2314006A40D8 /* EditedRangeSet.swift */; };
2A8E47E5299A2401006A40D8 /* EditedRangeSetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E4299A2401006A40D8 /* EditedRangeSetTests.swift */; };
2A8E47E7299B2F5C006A40D8 /* NSRangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E6299B2F5C006A40D8 /* NSRangeTests.swift */; };
2A8E47E9299C6064006A40D8 /* NSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E8299C6064006A40D8 /* NSRange.swift */; };
2A8E47EA299C6064006A40D8 /* NSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8E47E8299C6064006A40D8 /* NSRange.swift */; };
2A8EAE3E2BA3B15D00448875 /* Credits.json in Resources */ = {isa = PBXBuildFile; fileRef = 2A8EAE3D2BA3B15D00448875 /* Credits.json */; };
2A8EAE3F2BA3B15D00448875 /* Credits.json in Resources */ = {isa = PBXBuildFile; fileRef = 2A8EAE3D2BA3B15D00448875 /* Credits.json */; };
2A8EAE412BA3C3DC00448875 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8EAE402BA3C3DC00448875 /* AboutView.swift */; };
2A8EAE422BA3C3DC00448875 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8EAE402BA3C3DC00448875 /* AboutView.swift */; };
2A8EAE562BA7E2BE00448875 /* Licenses in Resources */ = {isa = PBXBuildFile; fileRef = 2A8EAE552BA7E2BE00448875 /* Licenses */; };
2A8EAE572BA7E2BE00448875 /* Licenses in Resources */ = {isa = PBXBuildFile; fileRef = 2A8EAE552BA7E2BE00448875 /* Licenses */; };
2A8EF014241F0A8A001BDBC0 /* StringLineProcessingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8EF013241F0A8A001BDBC0 /* StringLineProcessingTests.swift */; };
2A9003B9267715E600EC766F /* NSApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9003B8267715E500EC766F /* NSApplication.swift */; };
2A9003BA267715E600EC766F /* NSApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9003B8267715E500EC766F /* NSApplication.swift */; };
2A902A9D2B86DC140053FC96 /* KeyBindingsSettings.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2A1E7DCB2B889A1F004F0C07 /* KeyBindingsSettings.xcstrings */; };
@ -481,6 +474,8 @@
2A91C3191D1BE91E007CF8BE /* DefaultSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A91C3171D1BE91E007CF8BE /* DefaultSettings.swift */; };
2A91C31B1D1BFE47007CF8BE /* UTType+SettingFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A91C31A1D1BFE47007CF8BE /* UTType+SettingFile.swift */; };
2A91C31C1D1BFE47007CF8BE /* UTType+SettingFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A91C31A1D1BFE47007CF8BE /* UTType+SettingFile.swift */; };
2A929DB12C224CC700DED8C6 /* TextEditing in Frameworks */ = {isa = PBXBuildFile; productRef = 2A929DB02C224CC700DED8C6 /* TextEditing */; };
2A929DB32C224CCE00DED8C6 /* TextEditing in Frameworks */ = {isa = PBXBuildFile; productRef = 2A929DB22C224CCE00DED8C6 /* TextEditing */; };
2A938ACC297E4BA9007FBE5F /* SettingsPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A938ACB297E4BA9007FBE5F /* SettingsPane.swift */; };
2A938ACD297E4BA9007FBE5F /* SettingsPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A938ACB297E4BA9007FBE5F /* SettingsPane.swift */; };
2A938ACF297E4D7B007FBE5F /* SettingsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A938ACE297E4D7B007FBE5F /* SettingsWindowController.swift */; };
@ -497,12 +492,7 @@
2A9BC2792BDE00B1008B58B5 /* Donation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BC2772BDE00B1008B58B5 /* Donation.swift */; };
2A9BF3C41D382BB100E3D3E2 /* EditorTextView+Transformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BF3C31D382BB100E3D3E2 /* EditorTextView+Transformation.swift */; };
2A9BF3C51D382BB100E3D3E2 /* EditorTextView+Transformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BF3C31D382BB100E3D3E2 /* EditorTextView+Transformation.swift */; };
2A9BF3C71D38325200E3D3E2 /* String+FullwidthTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BF3C61D38325200E3D3E2 /* String+FullwidthTransform.swift */; };
2A9BF3C81D38325200E3D3E2 /* String+FullwidthTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9BF3C61D38325200E3D3E2 /* String+FullwidthTransform.swift */; };
2A9C07561CF9F982006D672D /* IncompatibleCharacterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9C07551CF9F982006D672D /* IncompatibleCharacterTests.swift */; };
2A9C370B1D66E99400774BA4 /* Pair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9C370A1D66E99400774BA4 /* Pair.swift */; };
2A9C370C1D66E99400774BA4 /* Pair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9C370A1D66E99400774BA4 /* Pair.swift */; };
2A9C370E1D672A1F00774BA4 /* BracePairTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9C370D1D672A1F00774BA4 /* BracePairTests.swift */; };
2AA056AD26FCA171000E0CB2 /* Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA056AC26FCA171000E0CB2 /* Arithmetics.swift */; };
2AA056AE26FCA171000E0CB2 /* Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA056AC26FCA171000E0CB2 /* Arithmetics.swift */; };
2AA106B02470F05F00979CB7 /* EncodingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5DCE881D18FFDB00D5D74C /* EncodingListView.swift */; };
@ -518,7 +508,6 @@
2AA175FB2AC5634500F6462C /* PopoverHolderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA175F92AC5634500F6462C /* PopoverHolderView.swift */; };
2AA2C6FC24399A920017D1EC /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 2AA2C6FB24399A920017D1EC /* Yams */; };
2AA2C6FE24399AA20017D1EC /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 2AA2C6FD24399AA20017D1EC /* Yams */; };
2AA2E0261C0454730087BDD6 /* StringIndentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA2E0251C0454730087BDD6 /* StringIndentationTests.swift */; };
2AA375471D40BDCB0080C27C /* LineEnding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA375461D40BDCB0080C27C /* LineEnding.swift */; };
2AA375481D40BDCB0080C27C /* LineEnding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA375461D40BDCB0080C27C /* LineEnding.swift */; };
2AA45A4B1D2E871900A1A401 /* EditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA45A4A1D2E871900A1A401 /* EditorViewController.swift */; };
@ -533,8 +522,6 @@
2AA4EE3E28D55CE80014B045 /* DelegateContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA4EE3C28D55CE80014B045 /* DelegateContext.swift */; };
2AA4F6A020A1C190003FD515 /* NSTextView+RoundedBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA4F69F20A1C190003FD515 /* NSTextView+RoundedBackground.swift */; };
2AA4F6A120A1C190003FD515 /* NSTextView+RoundedBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA4F69F20A1C190003FD515 /* NSTextView+RoundedBackground.swift */; };
2AA5BCFA24FFB21C00618F83 /* String+Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA5BCF924FFB21C00618F83 /* String+Match.swift */; };
2AA5BCFB24FFB21C00618F83 /* String+Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA5BCF924FFB21C00618F83 /* String+Match.swift */; };
2AA672592B8F719900B8F7E6 /* Script.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AA672582B8F6D7B00B8F7E6 /* Script.xcstrings */; };
2AA6725A2B8F719900B8F7E6 /* Script.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AA672582B8F6D7B00B8F7E6 /* Script.xcstrings */; };
2AA6726C2B8F75CC00B8F7E6 /* FileDropItem.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AA6726B2B8F75CC00B8F7E6 /* FileDropItem.xcstrings */; };
@ -560,8 +547,6 @@
2AA749C41D3C263300850802 /* DocumentWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA749C21D3C263300850802 /* DocumentWindowController.swift */; };
2AA761351D45634400031AAF /* String+Counting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA761341D45634400031AAF /* String+Counting.swift */; };
2AA761361D45634400031AAF /* String+Counting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA761341D45634400031AAF /* String+Counting.swift */; };
2AA7613A1D457BD500031AAF /* String+Indentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA761391D457BD500031AAF /* String+Indentation.swift */; };
2AA7613B1D457BD500031AAF /* String+Indentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA761391D457BD500031AAF /* String+Indentation.swift */; };
2AA79C7821CB7251005AD6AD /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA79C7721CB7251005AD6AD /* SettingsWindow.swift */; };
2AA79C7921CB7251005AD6AD /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA79C7721CB7251005AD6AD /* SettingsWindow.swift */; };
2AA7BDD62C1B0CC10075BB6C /* UnicodeNormalization in Frameworks */ = {isa = PBXBuildFile; productRef = 2AA7BDD52C1B0CC10075BB6C /* UnicodeNormalization */; };
@ -587,8 +572,6 @@
2AAD61F51D2BA0E0008FE772 /* OutlineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAD61F31D2BA0E0008FE772 /* OutlineItem.swift */; };
2AAD61F81D2BA3F5008FE772 /* HighlightParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAD61F71D2BA3F5008FE772 /* HighlightParser.swift */; };
2AAD61F91D2BA3F5008FE772 /* HighlightParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAD61F71D2BA3F5008FE772 /* HighlightParser.swift */; };
2AAD61FC1D2BD102008FE772 /* String+LineRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAD61FB1D2BD102008FE772 /* String+LineRange.swift */; };
2AAD61FD1D2BD102008FE772 /* String+LineRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAD61FB1D2BD102008FE772 /* String+LineRange.swift */; };
2AAE8E622AF8AE3B008954B5 /* Syntax+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAE8E612AF8AE3B008954B5 /* Syntax+Codable.swift */; };
2AAE8E632AF8AE3B008954B5 /* Syntax+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAE8E612AF8AE3B008954B5 /* Syntax+Codable.swift */; };
2AAF93562A73DEE600CCC4A7 /* LineEnding.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AAF93552A73DEE600CCC4A7 /* LineEnding.xcstrings */; };
@ -619,12 +602,6 @@
2ABF49E4221A54AD00239278 /* TextClipping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF49E2221A54AD00239278 /* TextClipping.swift */; };
2ABF86BD208C3C630082D52B /* AudioToolbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF86BC208C3C630082D52B /* AudioToolbox.swift */; };
2ABF86BE208C3C630082D52B /* AudioToolbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF86BC208C3C630082D52B /* AudioToolbox.swift */; };
2ABF9E932C1E8CFF0033D5E6 /* EditingContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E922C1E8CFB0033D5E6 /* EditingContext.swift */; };
2ABF9E942C1E8CFF0033D5E6 /* EditingContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E922C1E8CFB0033D5E6 /* EditingContext.swift */; };
2ABF9E962C1E8D7E0033D5E6 /* String+LineProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E952C1E8D780033D5E6 /* String+LineProcessing.swift */; };
2ABF9E972C1E8D7E0033D5E6 /* String+LineProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E952C1E8D780033D5E6 /* String+LineProcessing.swift */; };
2ABF9E9C2C1EC2A50033D5E6 /* String+Escaping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E9B2C1EC29D0033D5E6 /* String+Escaping.swift */; };
2ABF9E9D2C1EC2A50033D5E6 /* String+Escaping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E9B2C1EC29D0033D5E6 /* String+Escaping.swift */; };
2ABF9E9F2C1EC8620033D5E6 /* String+Filename.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E9E2C1EC8590033D5E6 /* String+Filename.swift */; };
2ABF9EA02C1EC8620033D5E6 /* String+Filename.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9E9E2C1EC8590033D5E6 /* String+Filename.swift */; };
2ABF9EA22C1ED4BF0033D5E6 /* String+Commenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABF9EA12C1ED4B90033D5E6 /* String+Commenting.swift */; };
@ -648,7 +625,7 @@
2AC6BFD221D00ABD00FF325C /* NSTextView+RegexParse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC6BFD021D00ABD00FF325C /* NSTextView+RegexParse.swift */; };
2AC7044824EBB76B00454706 /* NSToolbarItem+Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC7044724EBB76B00454706 /* NSToolbarItem+Validatable.swift */; };
2AC7044924EBB76B00454706 /* NSToolbarItem+Validatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC7044724EBB76B00454706 /* NSToolbarItem+Validatable.swift */; };
2AC71DE21BF0BDBC002E1434 /* StringExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC71DE11BF0BDBC002E1434 /* StringExtensionsTests.swift */; };
2AC71DE21BF0BDBC002E1434 /* StringAdvancedCountTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC71DE11BF0BDBC002E1434 /* StringAdvancedCountTests.swift */; };
2AC72EA2253478D5001D3CA0 /* FileDropItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC72EA1253478D5001D3CA0 /* FileDropItemTests.swift */; };
2AC94B3B2B6EAAE90086F9F2 /* RegexReference.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AC94B3A2B6EAAE90086F9F2 /* RegexReference.xcstrings */; };
2AC94B3C2B6EAAE90086F9F2 /* RegexReference.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2AC94B3A2B6EAAE90086F9F2 /* RegexReference.xcstrings */; };
@ -728,11 +705,7 @@
2ADBC91621C9F30000B884FF /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ADBC91421C9F30000B884FF /* Atomic.swift */; };
2ADD0AD8217A967200F78732 /* NSTextView+LineNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ADD0AD7217A967200F78732 /* NSTextView+LineNumber.swift */; };
2ADD0AD9217A967200F78732 /* NSTextView+LineNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ADD0AD7217A967200F78732 /* NSTextView+LineNumber.swift */; };
2ADF96412C1B05CD00B6B722 /* FileEncoding in Frameworks */ = {isa = PBXBuildFile; productRef = 2ADF96402C1B05CD00B6B722 /* FileEncoding */; };
2ADF96432C1B05D300B6B722 /* FileEncoding in Frameworks */ = {isa = PBXBuildFile; productRef = 2ADF96422C1B05D300B6B722 /* FileEncoding */; };
2AE12DFB1E7DB47000681F72 /* Collection+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFA1E7DB47000681F72 /* Collection+String.swift */; };
2AE12DFC1E7DB47000681F72 /* Collection+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFA1E7DB47000681F72 /* Collection+String.swift */; };
2AE12DFE1E7DB7D200681F72 /* StringCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFD1E7DB7D200681F72 /* StringCollectionTests.swift */; };
2AE12DFE1E7DB7D200681F72 /* StringFilenameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFD1E7DB7D200681F72 /* StringFilenameTests.swift */; };
2AE12E001E7DDB1B00681F72 /* EditorTextView+SurroundSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFF1E7DDB1B00681F72 /* EditorTextView+SurroundSelection.swift */; };
2AE12E011E7DDB1B00681F72 /* EditorTextView+SurroundSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12DFF1E7DDB1B00681F72 /* EditorTextView+SurroundSelection.swift */; };
2AE12E071E7DDF0700681F72 /* CustomSurroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE12E061E7DDF0700681F72 /* CustomSurroundView.swift */; };
@ -896,6 +869,7 @@
2A1125C523F6EFB2006A1DB2 /* URLDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLDetector.swift; sourceTree = "<group>"; };
2A11F2121E669BFA005E1675 /* PointerBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointerBridge.swift; sourceTree = "<group>"; };
2A1311D52127DCE1001D52C5 /* NSTextView+CurrentLineHighlighting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+CurrentLineHighlighting.swift"; sourceTree = "<group>"; };
2A1380062C225FAF00093BF3 /* NSTextStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSTextStorageTests.swift; sourceTree = "<group>"; };
2A158C1B2945A6B1000A4EC1 /* HeadingMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadingMenuItem.swift; sourceTree = "<group>"; };
2A158C1E2945E423000A4EC1 /* SavePanelAccessory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavePanelAccessory.swift; sourceTree = "<group>"; };
2A158C212945F54B000A4EC1 /* EditorOpacityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorOpacityView.swift; sourceTree = "<group>"; };
@ -957,7 +931,6 @@
2A2792971D1E57DA00F3FC5D /* SyntaxListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyntaxListViewController.swift; sourceTree = "<group>"; };
2A2B085F28046E3B0028D733 /* WarningInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningInspectorView.swift; sourceTree = "<group>"; };
2A2E56D62C018ADB00416F9E /* ComparableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparableTests.swift; sourceTree = "<group>"; };
2A2E56DA2C057FBF00416F9E /* BracePair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BracePair.swift; sourceTree = "<group>"; };
2A2EEF172B778BB1001FEDFB /* WrappingHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappingHStack.swift; sourceTree = "<group>"; };
2A30C7DA2B1380BE002F6381 /* ShortcutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutView.swift; sourceTree = "<group>"; };
2A33D07D1D1C75B8005977B9 /* SyntaxValidationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyntaxValidationView.swift; sourceTree = "<group>"; };
@ -1070,14 +1043,12 @@
2A6FD9D71D38C94100A59784 /* EditorTextView+Indenting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EditorTextView+Indenting.swift"; sourceTree = "<group>"; };
2A6FD9E61D394F5900A59784 /* LayoutManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutManager.swift; sourceTree = "<group>"; };
2A6FD9E91D3A819500A59784 /* EditorTextView+Commenting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EditorTextView+Commenting.swift"; sourceTree = "<group>"; };
2A6FD9EC1D3A85D700A59784 /* NSString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSString.swift; sourceTree = "<group>"; };
2A6FD9F51D3AE29E00A59784 /* Syntax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Syntax.swift; sourceTree = "<group>"; };
2A715E21261AC5960060CF84 /* CotEditor-Sparkle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "CotEditor-Sparkle.entitlements"; sourceTree = "<group>"; };
2A719F6523CD92370026F877 /* FuzzyRangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FuzzyRangeTests.swift; sourceTree = "<group>"; };
2A71BC7A1DDC50530085AE1C /* DocumentViewController+TouchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DocumentViewController+TouchBar.swift"; sourceTree = "<group>"; };
2A71BC7D1DDC70A80085AE1C /* NSImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
2A72DA0F209B778B005242B9 /* NSTextView+MultiCursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+MultiCursor.swift"; sourceTree = "<group>"; };
2A733E8820BBB4AC0090D7CB /* String+Case.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Case.swift"; sourceTree = "<group>"; };
2A73B9322A8F6620002F3A16 /* RegexTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegexTextField.swift; sourceTree = "<group>"; };
2A7470682B12FA5700669A7B /* NSTextStorage+TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextStorage+TextView.swift"; sourceTree = "<group>"; };
2A75ACCA19E86DDB00444894 /* CotEditor.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CotEditor.sdef; sourceTree = "<group>"; };
@ -1108,12 +1079,9 @@
2A8DA9461D28ED93003D0C4B /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
2A8E47E1299A2314006A40D8 /* EditedRangeSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditedRangeSet.swift; sourceTree = "<group>"; };
2A8E47E4299A2401006A40D8 /* EditedRangeSetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditedRangeSetTests.swift; sourceTree = "<group>"; };
2A8E47E6299B2F5C006A40D8 /* NSRangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSRangeTests.swift; sourceTree = "<group>"; };
2A8E47E8299C6064006A40D8 /* NSRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSRange.swift; sourceTree = "<group>"; };
2A8EAE3D2BA3B15D00448875 /* Credits.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Credits.json; sourceTree = "<group>"; };
2A8EAE402BA3C3DC00448875 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
2A8EAE552BA7E2BE00448875 /* Licenses */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Licenses; sourceTree = "<group>"; };
2A8EF013241F0A8A001BDBC0 /* StringLineProcessingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringLineProcessingTests.swift; sourceTree = "<group>"; };
2A9003B8267715E500EC766F /* NSApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSApplication.swift; sourceTree = "<group>"; };
2A902B99236E3AA600A6A9BB /* StringCommentingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCommentingTests.swift; sourceTree = "<group>"; };
2A9082E11D32456300228F50 /* NSTextView+Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextView+Layout.swift"; sourceTree = "<group>"; };
@ -1131,17 +1099,13 @@
2A9B134D27E2D84E009954A4 /* NSDraggingInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSDraggingInfo.swift; sourceTree = "<group>"; };
2A9BC2772BDE00B1008B58B5 /* Donation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Donation.swift; sourceTree = "<group>"; };
2A9BF3C31D382BB100E3D3E2 /* EditorTextView+Transformation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EditorTextView+Transformation.swift"; sourceTree = "<group>"; };
2A9BF3C61D38325200E3D3E2 /* String+FullwidthTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+FullwidthTransform.swift"; sourceTree = "<group>"; };
2A9C07551CF9F982006D672D /* IncompatibleCharacterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncompatibleCharacterTests.swift; sourceTree = "<group>"; };
2A9C370A1D66E99400774BA4 /* Pair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pair.swift; sourceTree = "<group>"; };
2A9C370D1D672A1F00774BA4 /* BracePairTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BracePairTests.swift; sourceTree = "<group>"; };
2AA056AC26FCA171000E0CB2 /* Arithmetics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arithmetics.swift; sourceTree = "<group>"; };
2AA14CF71FA47E8900EAF586 /* ScriptDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptDescriptor.swift; sourceTree = "<group>"; };
2AA14CFB1FA4983500EAF586 /* AppleScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScript.swift; sourceTree = "<group>"; };
2AA14CFE1FA498E900EAF586 /* UnixScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnixScript.swift; sourceTree = "<group>"; };
2AA14D011FA4999200EAF586 /* PersistentOSAScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentOSAScript.swift; sourceTree = "<group>"; };
2AA175F92AC5634500F6462C /* PopoverHolderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverHolderView.swift; sourceTree = "<group>"; };
2AA2E0251C0454730087BDD6 /* StringIndentationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringIndentationTests.swift; sourceTree = "<group>"; };
2AA375461D40BDCB0080C27C /* LineEnding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineEnding.swift; sourceTree = "<group>"; };
2AA45A4A1D2E871900A1A401 /* EditorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorViewController.swift; sourceTree = "<group>"; };
2AA45A501D2E938500A1A401 /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
@ -1149,7 +1113,6 @@
2AA4D3731D1AA0AC001D261D /* KeyBindingsSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBindingsSettingsView.swift; sourceTree = "<group>"; };
2AA4EE3C28D55CE80014B045 /* DelegateContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelegateContext.swift; sourceTree = "<group>"; };
2AA4F69F20A1C190003FD515 /* NSTextView+RoundedBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+RoundedBackground.swift"; sourceTree = "<group>"; };
2AA5BCF924FFB21C00618F83 /* String+Match.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Match.swift"; sourceTree = "<group>"; };
2AA672582B8F6D7B00B8F7E6 /* Script.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Script.xcstrings; sourceTree = "<group>"; };
2AA6726B2B8F75CC00B8F7E6 /* FileDropItem.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = FileDropItem.xcstrings; sourceTree = "<group>"; };
2AA6727D2B8F784700B8F7E6 /* Snippet.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Snippet.xcstrings; sourceTree = "<group>"; };
@ -1164,7 +1127,6 @@
2AA71A522BE366520084EC0A /* Observation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observation.swift; sourceTree = "<group>"; };
2AA749C21D3C263300850802 /* DocumentWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentWindowController.swift; sourceTree = "<group>"; };
2AA761341D45634400031AAF /* String+Counting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Counting.swift"; sourceTree = "<group>"; };
2AA761391D457BD500031AAF /* String+Indentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Indentation.swift"; sourceTree = "<group>"; };
2AA79C7721CB7251005AD6AD /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = "<group>"; };
2AA7BDDA2C1B10C80075BB6C /* UnicodeNormalizationForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnicodeNormalizationForm.swift; sourceTree = "<group>"; };
2AA7E97C1DBAAC950083B7ED /* Script.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Script.swift; sourceTree = "<group>"; };
@ -1176,7 +1138,6 @@
2AAD61EF1D2B0856008FE772 /* FuzzyRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FuzzyRange.swift; sourceTree = "<group>"; };
2AAD61F31D2BA0E0008FE772 /* OutlineItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutlineItem.swift; sourceTree = "<group>"; };
2AAD61F71D2BA3F5008FE772 /* HighlightParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightParser.swift; sourceTree = "<group>"; };
2AAD61FB1D2BD102008FE772 /* String+LineRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+LineRange.swift"; sourceTree = "<group>"; };
2AAE8E612AF8AE3B008954B5 /* Syntax+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Syntax+Codable.swift"; sourceTree = "<group>"; };
2AAF93552A73DEE600CCC4A7 /* LineEnding.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = LineEnding.xcstrings; sourceTree = "<group>"; };
2AAFA7BB2B7A2DAF00A2B228 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MultipleReplaceListView.storyboard; sourceTree = "<group>"; };
@ -1195,9 +1156,6 @@
2ABEFB6923DC0CA0008769F4 /* EditorCounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorCounterTests.swift; sourceTree = "<group>"; };
2ABF49E2221A54AD00239278 /* TextClipping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextClipping.swift; sourceTree = "<group>"; };
2ABF86BC208C3C630082D52B /* AudioToolbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioToolbox.swift; sourceTree = "<group>"; };
2ABF9E922C1E8CFB0033D5E6 /* EditingContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditingContext.swift; sourceTree = "<group>"; };
2ABF9E952C1E8D780033D5E6 /* String+LineProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+LineProcessing.swift"; sourceTree = "<group>"; };
2ABF9E9B2C1EC29D0033D5E6 /* String+Escaping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Escaping.swift"; sourceTree = "<group>"; };
2ABF9E9E2C1EC8590033D5E6 /* String+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Filename.swift"; sourceTree = "<group>"; };
2ABF9EA12C1ED4B90033D5E6 /* String+Commenting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Commenting.swift"; sourceTree = "<group>"; };
2AC13A0824F112D800799A93 /* CommandLineToolManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandLineToolManager.swift; sourceTree = "<group>"; };
@ -1210,7 +1168,7 @@
2AC6BFD021D00ABD00FF325C /* NSTextView+RegexParse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+RegexParse.swift"; sourceTree = "<group>"; };
2AC7044724EBB76B00454706 /* NSToolbarItem+Validatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSToolbarItem+Validatable.swift"; sourceTree = "<group>"; };
2AC71DDF1BF0BDBC002E1434 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
2AC71DE11BF0BDBC002E1434 /* StringExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTests.swift; sourceTree = "<group>"; };
2AC71DE11BF0BDBC002E1434 /* StringAdvancedCountTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringAdvancedCountTests.swift; sourceTree = "<group>"; };
2AC72EA1253478D5001D3CA0 /* FileDropItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDropItemTests.swift; sourceTree = "<group>"; };
2AC94B3A2B6EAAE90086F9F2 /* RegexReference.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = RegexReference.xcstrings; sourceTree = "<group>"; };
2ACC21B11E52B7920078241F /* DefaultOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultOptions.swift; sourceTree = "<group>"; };
@ -1247,8 +1205,7 @@
2ADB04AB2A89F14D00C4F562 /* AddRemoveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRemoveButton.swift; sourceTree = "<group>"; };
2ADBC91421C9F30000B884FF /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
2ADD0AD7217A967200F78732 /* NSTextView+LineNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextView+LineNumber.swift"; sourceTree = "<group>"; };
2AE12DFA1E7DB47000681F72 /* Collection+String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Collection+String.swift"; sourceTree = "<group>"; };
2AE12DFD1E7DB7D200681F72 /* StringCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringCollectionTests.swift; sourceTree = "<group>"; };
2AE12DFD1E7DB7D200681F72 /* StringFilenameTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringFilenameTests.swift; sourceTree = "<group>"; };
2AE12DFF1E7DDB1B00681F72 /* EditorTextView+SurroundSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EditorTextView+SurroundSelection.swift"; sourceTree = "<group>"; };
2AE12E061E7DDF0700681F72 /* CustomSurroundView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomSurroundView.swift; sourceTree = "<group>"; };
2AE144B52B00A963005E8CF1 /* Identifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identifiable.swift; sourceTree = "<group>"; };
@ -1322,13 +1279,14 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2A13800C2C23076300093BF3 /* FileEncoding in Frameworks */,
2A3268932C1C580800CF1AAF /* Defaults in Frameworks */,
2A38536A2C1AF43100C282C0 /* FilePermissions in Frameworks */,
2ACD02BF22A87F0400893051 /* ColorCode in Frameworks */,
2AA7BDD62C1B0CC10075BB6C /* UnicodeNormalization in Frameworks */,
2AA2C6FC24399A920017D1EC /* Yams in Frameworks */,
2A32688E2C1B504500CF1AAF /* Shortcut in Frameworks */,
2ADF96412C1B05CD00B6B722 /* FileEncoding in Frameworks */,
2A929DB12C224CC700DED8C6 /* TextEditing in Frameworks */,
2A7E06E82C1A745400E5396D /* CharacterInfo in Frameworks */,
2ACAAC1C2B85E74C0041B095 /* SyntaxMap in Frameworks */,
);
@ -1338,6 +1296,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2A13800D2C23076300093BF3 /* FileEncoding in Frameworks */,
2A7E06EA2C1A745E00E5396D /* CharacterInfo in Frameworks */,
2A3853682C1AF42C00C282C0 /* FilePermissions in Frameworks */,
2A3268902C1B504B00CF1AAF /* Shortcut in Frameworks */,
@ -1345,8 +1304,8 @@
2A3268952C1C580D00CF1AAF /* Defaults in Frameworks */,
2AAAE6E526DB82F800C5F0AC /* Sparkle in Frameworks */,
2ACD02BD22A87EFD00893051 /* ColorCode in Frameworks */,
2ADF96432C1B05D300B6B722 /* FileEncoding in Frameworks */,
2ACAAC1E2B85E7530041B095 /* SyntaxMap in Frameworks */,
2A929DB32C224CCE00DED8C6 /* TextEditing in Frameworks */,
2AA2C6FE24399AA20017D1EC /* Yams in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -1663,20 +1622,10 @@
2A9082E41D324D9A00228F50 /* Geometry.swift */,
2AF0C1241D3DA44900B6FCB6 /* FourCharCode.swift */,
2ADA15ED21C5073D00C6608B /* Collection+IndexSet.swift */,
2AE12DFA1E7DB47000681F72 /* Collection+String.swift */,
2A2792941D1DBDAC00F3FC5D /* String+Constants.swift */,
2ABF9E9B2C1EC29D0033D5E6 /* String+Escaping.swift */,
2AAD61FB1D2BD102008FE772 /* String+LineRange.swift */,
2AA761341D45634400031AAF /* String+Counting.swift */,
2AA5BCF924FFB21C00618F83 /* String+Match.swift */,
2A9BF3C61D38325200E3D3E2 /* String+FullwidthTransform.swift */,
2A733E8820BBB4AC0090D7CB /* String+Case.swift */,
2AA761391D457BD500031AAF /* String+Indentation.swift */,
2ABF9EA12C1ED4B90033D5E6 /* String+Commenting.swift */,
2ABF9E952C1E8D780033D5E6 /* String+LineProcessing.swift */,
2ABF9E9E2C1EC8590033D5E6 /* String+Filename.swift */,
2A8E47E8299C6064006A40D8 /* NSRange.swift */,
2A6FD9EC1D3A85D700A59784 /* NSString.swift */,
2AE3F3171D3F8A1F005B8724 /* NSAttributedString.swift */,
2AF1D85721B8D9250060BC04 /* NSRegularExpression+Additions.swift */,
2A8DA9461D28ED93003D0C4B /* URL.swift */,
@ -1883,7 +1832,6 @@
2A505C07298952E5002080AA /* KeyBinding */,
2A78F571298C90520084B8B4 /* Snippet */,
2AC6BFCF21D00A8500FF325C /* Regex Parser */,
2ABF9E922C1E8CFB0033D5E6 /* EditingContext.swift */,
2AA375461D40BDCB0080C27C /* LineEnding.swift */,
2A8C338E1D3E1C040005B0B7 /* IncompatibleCharacter.swift */,
2AAD61EF1D2B0856008FE772 /* FuzzyRange.swift */,
@ -1893,8 +1841,6 @@
2ACF23AD26302A4C002B5E10 /* Theme+Syntax.swift */,
2A1E7DD32B8C5A23004F0C07 /* Mode.swift */,
2AB857EA2B93050E0079CFA2 /* ModeOptions.swift */,
2A9C370A1D66E99400774BA4 /* Pair.swift */,
2A2E56DA2C057FBF00416F9E /* BracePair.swift */,
2A7FCC45280A367C0070EAB3 /* ValueRange.swift */,
2ABF49E2221A54AD00239278 /* TextClipping.swift */,
2A1893A91FFF422D00AD244F /* LineSort.swift */,
@ -2220,18 +2166,16 @@
2A2E56D62C018ADB00416F9E /* ComparableTests.swift */,
2A9082EE1D325ED900228F50 /* GeometryTests.swift */,
2AC39F721E8AC80E009F97D5 /* CollectionTests.swift */,
2AE12DFD1E7DB7D200681F72 /* StringCollectionTests.swift */,
2AC71DE11BF0BDBC002E1434 /* StringExtensionsTests.swift */,
2AA2E0251C0454730087BDD6 /* StringIndentationTests.swift */,
2AE12DFD1E7DB7D200681F72 /* StringFilenameTests.swift */,
2A902B99236E3AA600A6A9BB /* StringCommentingTests.swift */,
2A8EF013241F0A8A001BDBC0 /* StringLineProcessingTests.swift */,
2AC71DE11BF0BDBC002E1434 /* StringAdvancedCountTests.swift */,
2A18A5BC1C4A730D00BAD817 /* EncodingTests.swift */,
2A476CAD1D09C8C80088E37A /* URLExtensionsTests.swift */,
2AFD218C27E0442B00E83E88 /* UTTypeExtensionTests.swift */,
2A476CB01D09D0500088E37A /* FontExtensionTests.swift */,
2AF0C1271D3DA6F800B6FCB6 /* FourCharCodeTests.swift */,
2A8E47E6299B2F5C006A40D8 /* NSRangeTests.swift */,
2AEBD259246BB4C200EC97A3 /* NSAttributedStringTests.swift */,
2A1380062C225FAF00093BF3 /* NSTextStorageTests.swift */,
2A89160B2394B87100AC13EE /* NSLayoutManagerTests.swift */,
);
name = Extensions;
@ -2244,7 +2188,6 @@
2ACC65311C98033D000574DC /* ThemeTests.swift */,
2A9C07551CF9F982006D672D /* IncompatibleCharacterTests.swift */,
2A54BE2B1D40EB24000816B0 /* LineEndingTests.swift */,
2A9C370D1D672A1F00774BA4 /* BracePairTests.swift */,
2AED46721E43942300751C45 /* TextFindTests.swift */,
2A1893AC1FFF6A0100AD244F /* LineSortTests.swift */,
2A7B279824E435FE00F02304 /* OutlineTests.swift */,
@ -2423,6 +2366,7 @@
2ADF96402C1B05CD00B6B722 /* FileEncoding */,
2AA7BDD52C1B0CC10075BB6C /* UnicodeNormalization */,
2A32688D2C1B504500CF1AAF /* Shortcut */,
2A929DB02C224CC700DED8C6 /* TextEditing */,
);
productInstallPath = "$(HOME)/Applications";
productName = CotEditor;
@ -2474,6 +2418,7 @@
2A3268942C1C580D00CF1AAF /* Defaults */,
2AA7BDD72C1B0CC70075BB6C /* UnicodeNormalization */,
2A32688F2C1B504B00CF1AAF /* Shortcut */,
2A929DB22C224CCE00DED8C6 /* TextEditing */,
);
productInstallPath = "$(HOME)/Applications";
productName = CotEditor;
@ -2854,7 +2799,6 @@
2A1ABCA527F079120054795D /* BidiScroller.swift in Sources */,
2A1ABC9B27F056E60054795D /* BidiScrollView.swift in Sources */,
2A231A291E7BD82700C2A909 /* Binding.swift in Sources */,
2A2E56DB2C057FBF00416F9E /* BracePair.swift in Sources */,
2AFECF5B2171C0E60065A7DE /* Bundle+AppInfo.swift in Sources */,
2A24F9132BEDF6D000CB6CCF /* CapsuleButtonStyle.swift in Sources */,
2AB1BD24287DA73D00C6FEAF /* CharacterCountOptionsSheetView.swift in Sources */,
@ -2864,7 +2808,6 @@
2A5ADE851D2168FC00F6CE26 /* Collection.swift in Sources */,
2A5C00342814698000700CAE /* Collection+BinarySearch.swift in Sources */,
2ADA15EF21C5073D00C6608B /* Collection+IndexSet.swift in Sources */,
2AE12DFC1E7DB47000681F72 /* Collection+String.swift in Sources */,
2A4257B11D22FD490086DAAD /* ColorCodePanelController.swift in Sources */,
2A1A4EB024FB9D9300B50AA0 /* Combine.swift in Sources */,
2A5E41052B0AEFBB00D5EA20 /* CommandBarView.swift in Sources */,
@ -2899,7 +2842,6 @@
2ACDC0921D1726BD009B72D6 /* DotView.swift in Sources */,
2A68722F288A5C44006D6B41 /* DraggableHostingView.swift in Sources */,
2A8E47E2299A2314006A40D8 /* EditedRangeSet.swift in Sources */,
2ABF9E942C1E8CFF0033D5E6 /* EditingContext.swift in Sources */,
2AD7B9B01D3E832E00E5D6D7 /* EditorCounter.swift in Sources */,
2A158C222945F54B000A4EC1 /* EditorOpacityView.swift in Sources */,
2AEC69C51D41A1BE0089F96F /* EditorTextView.swift in Sources */,
@ -3000,11 +2942,9 @@
2A484A3A236579A7006FFD14 /* NSLayoutManager+ValidationIgnorable.swift in Sources */,
2AEAA1432C00B37300B5332F /* NSMenu.swift in Sources */,
2AF99621235ACDD60041872E /* NSPrintInfo.swift in Sources */,
2A8E47E9299C6064006A40D8 /* NSRange.swift in Sources */,
2AF1D85921B8D9250060BC04 /* NSRegularExpression+Additions.swift in Sources */,
2A1ABCA827F07CED0054795D /* NSScroller.swift in Sources */,
2AA86283212ED91400BB75C9 /* NSSplitView+Autosave.swift in Sources */,
2A6FD9EE1D3A85D700A59784 /* NSString.swift in Sources */,
2AF63BA82A6FA4D900E1258E /* NSTableView.swift in Sources */,
2A180F4B2854E71800EBAF66 /* NSTextSelectionDataSource.swift in Sources */,
2ABBACA21E3F1D1C00A080E7 /* NSTextStorage+ScriptingSupport.swift in Sources */,
@ -3040,7 +2980,6 @@
2AE44DB82BE65C1F002A787D /* OutlineNavigator.swift in Sources */,
2A63A9D824E8C8F70017ACBB /* OutlinePicker.swift in Sources */,
2ACDC0981D172B2A009B72D6 /* PaddingTextFieldCell.swift in Sources */,
2A9C370C1D66E99400774BA4 /* Pair.swift in Sources */,
2A1893A81FFF16A400AD244F /* PatternSortView.swift in Sources */,
2AA14D031FA4999200EAF586 /* PersistentOSAScript.swift in Sources */,
2A11F2141E669BFA005E1675 /* PointerBridge.swift in Sources */,
@ -3078,17 +3017,10 @@
2A5D13261D1F9D4100D38E6A /* StatableToolbarItem.swift in Sources */,
2AD21FCD1D2E3BE80018C8D1 /* StatusBar.swift in Sources */,
2A26156E2977B87F008C2240 /* StepperNumberField.swift in Sources */,
2A733E8A20BBB4AC0090D7CB /* String+Case.swift in Sources */,
2ABF9EA32C1ED4BF0033D5E6 /* String+Commenting.swift in Sources */,
2A2792961D1DBDAC00F3FC5D /* String+Constants.swift in Sources */,
2AA761361D45634400031AAF /* String+Counting.swift in Sources */,
2ABF9E9D2C1EC2A50033D5E6 /* String+Escaping.swift in Sources */,
2ABF9EA02C1EC8620033D5E6 /* String+Filename.swift in Sources */,
2A9BF3C81D38325200E3D3E2 /* String+FullwidthTransform.swift in Sources */,
2AA7613B1D457BD500031AAF /* String+Indentation.swift in Sources */,
2ABF9E972C1E8D7E0033D5E6 /* String+LineProcessing.swift in Sources */,
2AAD61FD1D2BD102008FE772 /* String+LineRange.swift in Sources */,
2AA5BCFA24FFB21C00618F83 /* String+Match.swift in Sources */,
2A2615892977FCF6008C2240 /* SubmitButtonGroup.swift in Sources */,
2A1B7E76216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */,
2A6FD9F71D3AE29E00A59784 /* Syntax.swift in Sources */,
@ -3149,7 +3081,6 @@
buildActionMask = 2147483647;
files = (
2AF5D0E5286D9AB3000BE826 /* ArithmeticsTests.swift in Sources */,
2A9C370E1D672A1F00774BA4 /* BracePairTests.swift in Sources */,
2AC39F731E8AC80E009F97D5 /* CollectionTests.swift in Sources */,
2A2E56D72C018ADB00416F9E /* ComparableTests.swift in Sources */,
2A3F8F682429E04000CBBA89 /* DebouncerTests.swift in Sources */,
@ -3169,15 +3100,13 @@
2A1893AD1FFF6A0100AD244F /* LineSortTests.swift in Sources */,
2AEBD25A246BB4C200EC97A3 /* NSAttributedStringTests.swift in Sources */,
2A89160C2394B87100AC13EE /* NSLayoutManagerTests.swift in Sources */,
2A8E47E7299B2F5C006A40D8 /* NSRangeTests.swift in Sources */,
2A1380072C225FAF00093BF3 /* NSTextStorageTests.swift in Sources */,
2A7B279924E435FE00F02304 /* OutlineTests.swift in Sources */,
2AFD328F2949B34A000ED1C5 /* RegularExpressionSyntaxTests.swift in Sources */,
2A04E9BB27FD6911008C82D8 /* SnippetTests.swift in Sources */,
2AE12DFE1E7DB7D200681F72 /* StringCollectionTests.swift in Sources */,
2AC71DE21BF0BDBC002E1434 /* StringAdvancedCountTests.swift in Sources */,
2A902B9A236E3AA600A6A9BB /* StringCommentingTests.swift in Sources */,
2AC71DE21BF0BDBC002E1434 /* StringExtensionsTests.swift in Sources */,
2AA2E0261C0454730087BDD6 /* StringIndentationTests.swift in Sources */,
2A8EF014241F0A8A001BDBC0 /* StringLineProcessingTests.swift in Sources */,
2AE12DFE1E7DB7D200681F72 /* StringFilenameTests.swift in Sources */,
2A63CEC41D0B06D800ED8186 /* SyntaxTests.swift in Sources */,
2A5EDDBD241B64EB00A07810 /* TextClippingTests.swift in Sources */,
2AED46731E43942300751C45 /* TextFindTests.swift in Sources */,
@ -3206,7 +3135,6 @@
2A1ABCA627F079120054795D /* BidiScroller.swift in Sources */,
2A1ABC9C27F056E60054795D /* BidiScrollView.swift in Sources */,
2A231A281E7BD82700C2A909 /* Binding.swift in Sources */,
2A2E56DC2C057FBF00416F9E /* BracePair.swift in Sources */,
2AFECF5A2171C0E60065A7DE /* Bundle+AppInfo.swift in Sources */,
2AB1BD25287DA73D00C6FEAF /* CharacterCountOptionsSheetView.swift in Sources */,
2A24F9142BEDF6D000CB6CCF /* CapsuleButtonStyle.swift in Sources */,
@ -3216,7 +3144,6 @@
2A5ADE841D2168FC00F6CE26 /* Collection.swift in Sources */,
2A5C00352814698000700CAE /* Collection+BinarySearch.swift in Sources */,
2ADA15EE21C5073D00C6608B /* Collection+IndexSet.swift in Sources */,
2AE12DFB1E7DB47000681F72 /* Collection+String.swift in Sources */,
2A4257B01D22FD490086DAAD /* ColorCodePanelController.swift in Sources */,
2A1A4EB124FB9D9300B50AA0 /* Combine.swift in Sources */,
2A5E41062B0AEFBB00D5EA20 /* CommandBarView.swift in Sources */,
@ -3251,7 +3178,6 @@
2ACDC0911D1726BD009B72D6 /* DotView.swift in Sources */,
2A687230288A5C44006D6B41 /* DraggableHostingView.swift in Sources */,
2A8E47E3299A2314006A40D8 /* EditedRangeSet.swift in Sources */,
2ABF9E932C1E8CFF0033D5E6 /* EditingContext.swift in Sources */,
2AD7B9AF1D3E832E00E5D6D7 /* EditorCounter.swift in Sources */,
2A158C232945F54B000A4EC1 /* EditorOpacityView.swift in Sources */,
2AEC69C41D41A1BE0089F96F /* EditorTextView.swift in Sources */,
@ -3352,11 +3278,9 @@
2A484A39236579A7006FFD14 /* NSLayoutManager+ValidationIgnorable.swift in Sources */,
2AEAA1442C00B37300B5332F /* NSMenu.swift in Sources */,
2AF99620235ACDD60041872E /* NSPrintInfo.swift in Sources */,
2A8E47EA299C6064006A40D8 /* NSRange.swift in Sources */,
2AF1D85821B8D9250060BC04 /* NSRegularExpression+Additions.swift in Sources */,
2A1ABCA927F07CED0054795D /* NSScroller.swift in Sources */,
2AA86282212ED91400BB75C9 /* NSSplitView+Autosave.swift in Sources */,
2A6FD9ED1D3A85D700A59784 /* NSString.swift in Sources */,
2AF63BA92A6FA4D900E1258E /* NSTableView.swift in Sources */,
2A180F4C2854E71800EBAF66 /* NSTextSelectionDataSource.swift in Sources */,
2ABBACA11E3F1D1C00A080E7 /* NSTextStorage+ScriptingSupport.swift in Sources */,
@ -3392,7 +3316,6 @@
2AE44DB92BE65C1F002A787D /* OutlineNavigator.swift in Sources */,
2A63A9D924E8C8F70017ACBB /* OutlinePicker.swift in Sources */,
2ACDC0971D172B2A009B72D6 /* PaddingTextFieldCell.swift in Sources */,
2A9C370B1D66E99400774BA4 /* Pair.swift in Sources */,
2A1893A71FFF16A400AD244F /* PatternSortView.swift in Sources */,
2AA14D021FA4999200EAF586 /* PersistentOSAScript.swift in Sources */,
2A11F2131E669BFA005E1675 /* PointerBridge.swift in Sources */,
@ -3430,17 +3353,10 @@
2A5D13251D1F9D4100D38E6A /* StatableToolbarItem.swift in Sources */,
2AD21FCC1D2E3BE80018C8D1 /* StatusBar.swift in Sources */,
2A26156F2977B87F008C2240 /* StepperNumberField.swift in Sources */,
2A733E8920BBB4AC0090D7CB /* String+Case.swift in Sources */,
2ABF9EA22C1ED4BF0033D5E6 /* String+Commenting.swift in Sources */,
2A2792951D1DBDAC00F3FC5D /* String+Constants.swift in Sources */,
2AA761351D45634400031AAF /* String+Counting.swift in Sources */,
2ABF9E9C2C1EC2A50033D5E6 /* String+Escaping.swift in Sources */,
2ABF9E9F2C1EC8620033D5E6 /* String+Filename.swift in Sources */,
2A9BF3C71D38325200E3D3E2 /* String+FullwidthTransform.swift in Sources */,
2AA7613A1D457BD500031AAF /* String+Indentation.swift in Sources */,
2ABF9E962C1E8D7E0033D5E6 /* String+LineProcessing.swift in Sources */,
2AAD61FC1D2BD102008FE772 /* String+LineRange.swift in Sources */,
2AA5BCFB24FFB21C00618F83 /* String+Match.swift in Sources */,
2A26158A2977FCF6008C2240 /* SubmitButtonGroup.swift in Sources */,
2A1B7E75216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */,
2A6FD9F61D3AE29E00A59784 /* Syntax.swift in Sources */,
@ -3984,6 +3900,14 @@
isa = XCSwiftPackageProductDependency;
productName = CharacterInfo;
};
2A929DB02C224CC700DED8C6 /* TextEditing */ = {
isa = XCSwiftPackageProductDependency;
productName = TextEditing;
};
2A929DB22C224CCE00DED8C6 /* TextEditing */ = {
isa = XCSwiftPackageProductDependency;
productName = TextEditing;
};
2AA2C6FB24399A920017D1EC /* Yams */ = {
isa = XCSwiftPackageProductDependency;
package = 2AA2C6FA24399A920017D1EC /* XCRemoteSwiftPackageReference "Yams" */;

View File

@ -198,18 +198,6 @@ enum QuantityComparisonResult {
extension Sequence {
/// Counts up elements that satisfy the given predicate.
///
/// - Parameters:
/// - predicate: A closure that takes an element of the sequence as its argument
/// and returns a Boolean value indicating whether the element should be counted.
/// - Returns: The number of elements that satisfies the given predicate.
func count(where predicate: (Element) throws -> Bool) rethrows -> Int {
try self.filter(predicate).count
}
/// Counts up elements by enumerating collection until encountering the element that doesn't satisfy the given predicate.
///
/// - Parameters:

View File

@ -24,6 +24,7 @@
//
import SwiftUI
import TextEditing
struct CustomSurroundView: View {

View File

@ -24,6 +24,7 @@
//
import AppKit
import TextEditing
extension EditorTextView: Indenting {

View File

@ -25,6 +25,7 @@
import AppKit
import SwiftUI
import TextEditing
extension EditorTextView {

View File

@ -28,6 +28,7 @@ import AppKit
import Combine
import Defaults
import Shortcut
import TextEditing
private extension NSAttributedString.Key {

View File

@ -24,6 +24,7 @@
//
import Foundation
import TextEditing
protocol HighlightExtractable: Sendable {

View File

@ -25,6 +25,7 @@
//
import Foundation
import TextEditing
typealias Highlight = ValueRange<SyntaxType>

View File

@ -24,6 +24,7 @@
//
import AppKit
import TextEditing
extension NSTextView {

View File

@ -24,6 +24,7 @@
//
import AppKit
import TextEditing
extension NSTextView {

View File

@ -27,6 +27,7 @@ import SwiftUI
import Observation
import Combine
import Defaults
import TextEditing
final class OutlineInspectorViewController: NSHostingController<OutlineInspectorView>, DocumentOwner {

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2018-2022 1024jp
// © 2018-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@
//
import AppKit
import TextEditing
final class RegexFindPanelTextView: FindPanelTextView {

View File

@ -24,6 +24,7 @@
//
import Foundation
import TextEditing
extension String {

View File

@ -26,155 +26,6 @@
import Foundation
import UnicodeNormalization
extension StringProtocol {
/// The number of words in the whole string.
var numberOfWords: Int {
var count = 0
self.enumerateSubstrings(in: self.startIndex..<self.endIndex, options: [.byWords, .localized, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
return count
}
/// The number of lines in the whole string excluding the last blank line.
var numberOfLines: Int {
self.numberOfLines()
}
/// Calculates the line number at the given character index (1-based).
///
/// - Parameter index: The character index.
/// - Returns: The line number.
func lineNumber(at index: Index) -> Int {
guard !self.isEmpty, index > self.startIndex else { return 1 }
return self.numberOfLines(in: self.startIndex..<index, includesLastBreak: true)
}
/// Counts the number of lines in the given range.
///
/// - Parameters:
/// - range: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: Range<String.Index>? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.startIndex..<self.endIndex
if self.isEmpty || range.isEmpty { return 0 }
var count = 0
self.enumerateSubstrings(in: range, options: [.byLines, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
if includesLastBreak, self[range].last?.isNewline == true {
count += 1
}
return count
}
/// Counts the number of lines in the given ranges.
///
/// - Parameters:
/// - ranges: The character ranges to count lines.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in ranges: [Range<String.Index>], includesLastBreak: Bool = false) -> Int {
assert(!ranges.isEmpty)
if self.isEmpty || ranges.isEmpty { return 0 }
// use simple count for efficiency
if ranges.count == 1 {
return self.numberOfLines(in: ranges[0], includesLastBreak: includesLastBreak)
}
// evaluate line ranges to avoid double-count lines holding multiple ranges
var lineRanges: [Range<String.Index>] = []
for range in ranges {
let lineRange = self.lineRange(for: range)
self.enumerateSubstrings(in: lineRange, options: [.byLines, .substringNotRequired]) { (_, substringRange, _, _) in
lineRanges.append(substringRange)
}
if includesLastBreak, self[range].last?.isNewline == true {
lineRanges.append(self.lineRange(at: range.upperBound))
}
}
return lineRanges.uniqued.count
}
/// Calculates the number of characters from the beginning of the line where the given character index locates (0-based).
///
/// - Parameter index: The character index.
/// - Returns: The column number.
func columnNumber(at index: Index) -> Int {
self.distance(from: self.lineStartIndex(at: index), to: index)
}
}
// MARK: NSRange based
extension String {
/// Calculates the line number at the given character index (1-based).
///
/// - Parameter location: The UTF16-based character index.
/// - Returns: The line number.
func lineNumber(at location: Int) -> Int {
guard !self.isEmpty, location > 0 else { return 1 }
return self.numberOfLines(in: NSRange(location: 0, length: location), includesLastBreak: true)
}
/// Counts the number of lines in the given range.
///
/// - Parameters:
/// - ranges: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: NSRange? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.nsRange
if self.isEmpty || range.isEmpty { return 0 }
var count = 0
(self as NSString).enumerateSubstrings(in: range, options: [.byLines, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
if includesLastBreak, (self as NSString).character(at: range.upperBound - 1).isNewline == true {
count += 1
}
return count
}
}
// MARK: CharacterCountOptions
struct CharacterCountOptions {
enum CharacterUnit: String, Sendable, CaseIterable {

View File

@ -25,6 +25,7 @@
//
import Foundation
import TextEditing
enum SyntaxType: String, CaseIterable {

View File

@ -24,6 +24,7 @@
//
import Foundation
import TextEditing
struct TextFind {

View File

@ -14,6 +14,7 @@ let package = Package(
.library(name: "Defaults", targets: ["Defaults"]),
.library(name: "FileEncoding", targets: ["FileEncoding"]),
.library(name: "FilePermissions", targets: ["FilePermissions"]),
.library(name: "TextEditing", targets: ["TextEditing"]),
.library(name: "UnicodeNormalization", targets: ["UnicodeNormalization"]),
.library(name: "Shortcut", targets: ["Shortcut"]),
@ -31,6 +32,9 @@ let package = Package(
.target(name: "FilePermissions"),
.testTarget(name: "FilePermissionsTests", dependencies: ["FilePermissions"]),
.target(name: "TextEditing"),
.testTarget(name: "TextEditingTests", dependencies: ["TextEditing"]),
.target(name: "UnicodeNormalization"),
.testTarget(name: "UnicodeNormalizationTests", dependencies: ["UnicodeNormalization"]),

View File

@ -1,5 +1,6 @@
//
// BracePair.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,9 +26,9 @@
import Foundation
typealias BracePair = Pair<Character>
public typealias BracePair = Pair<Character>
extension Pair where T == Character {
public extension Pair where T == Character {
static let braces: [BracePair] = [BracePair("(", ")"),
BracePair("{", "}"),
@ -36,7 +37,7 @@ extension Pair where T == Character {
static let doubleQuotes = BracePair("\"", "\"")
enum PairIndex {
enum PairIndex: Equatable, Sendable {
case begin(String.Index)
case end(String.Index)
@ -45,7 +46,7 @@ extension Pair where T == Character {
}
extension StringProtocol {
public extension StringProtocol {
/// Finds the range enclosed by one of given brace pairs.
///

View File

@ -1,5 +1,6 @@
//
// Collection+String.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -27,16 +28,22 @@ import Foundation
// MARK: Sort
struct StringComparisonOptions: OptionSet {
public struct StringComparisonOptions: OptionSet, Equatable, Sendable {
let rawValue: Int
public let rawValue: Int
static let localized = Self(rawValue: 1 << 0)
static let caseInsensitive = Self(rawValue: 1 << 1)
public static let localized = Self(rawValue: 1 << 0)
public static let caseInsensitive = Self(rawValue: 1 << 1)
public init(rawValue: Int) {
self.rawValue = rawValue
}
}
extension MutableCollection where Self: RandomAccessCollection {
public extension MutableCollection where Self: RandomAccessCollection {
/// Sorts the collection in place, using the string value that the given key path refers as the comparison between elements.
///
@ -64,7 +71,7 @@ extension MutableCollection where Self: RandomAccessCollection {
}
extension Sequence {
public extension Sequence {
/// Returns the elements of the sequence, sorted using the string value that the given key path refers with the desired string comparison strategy.
///

View File

@ -0,0 +1,67 @@
//
// Collection.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2024-06-19.
//
// ---------------------------------------------------------------------------
//
// © 2016-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// MARK: - Unique
extension Sequence where Element: Equatable {
/// An array consists of unique elements of receiver by keeping ordering.
var uniqued: [Element] {
self.reduce(into: []) { (unique, element) in
guard !unique.contains(element) else { return }
unique.append(element)
}
}
}
// MARK: - Sort
extension Sequence {
/// Returns the elements of the sequence, sorted using the value that the given key path refers as the comparison between elements.
///
/// - Parameter keyPath: The key path to the value to compare.
/// - Returns: A sorted array of the sequences elements.
func sorted(_ keyPath: KeyPath<Element, some Comparable>) -> [Element] {
self.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
}
extension MutableCollection where Self: RandomAccessCollection {
/// Sorts the collection in place, using the value that the given key path refers as the comparison between elements.
///
/// - Parameter keyPath: The key path to the value to compare.
mutating func sort(_ keyPath: KeyPath<Element, some Comparable>) {
self.sort { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
}

View File

@ -1,5 +1,6 @@
//
// EditingContext.swift
// TextEditing
//
// CotEditor
// https://coteditor.com

View File

@ -1,5 +1,6 @@
//
// NSRange.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
extension NSRange {
public extension NSRange {
static let notFound = NSRange(location: NSNotFound, length: 0)
@ -89,13 +90,21 @@ extension NSRange {
extension NSRange {
public extension NSRange {
struct InsertionItem: Equatable {
struct InsertionItem: Equatable, Sendable {
var string: String
var location: Int
var forward: Bool
public var string: String
public var location: Int
public var forward: Bool
public init(string: String, location: Int, forward: Bool) {
self.string = string
self.location = location
self.forward = forward
}
}
@ -134,7 +143,7 @@ extension NSRange {
}
extension Sequence<NSRange> {
public extension Sequence<NSRange> {
/// The range that contains all ranges.
var union: NSRange? {
@ -149,7 +158,7 @@ extension Sequence<NSRange> {
}
extension IndexSet {
public extension IndexSet {
/// Initializes an index set with multiple NSRanges.
///

View File

@ -1,5 +1,6 @@
//
// String+NSRange.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation.NSString
extension String {
public extension String {
/// Copied string to make sure the string is not a kind of NSMutableString.
var immutable: String {
@ -35,7 +36,7 @@ extension String {
}
extension StringProtocol {
public extension StringProtocol {
/// Whole range in NSRange.
var nsRange: NSRange {
@ -51,7 +52,7 @@ extension StringProtocol {
}
extension NSString {
public extension NSString {
/// Whole range in NSRange
var range: NSRange {
@ -296,7 +297,7 @@ extension NSString {
}
extension unichar {
public extension unichar {
/// A Boolean value indicating whether this character represents a newline.
///

View File

@ -1,5 +1,6 @@
//
// Pair.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -23,13 +24,13 @@
// limitations under the License.
//
struct Pair<T> {
public struct Pair<T> {
var begin: T
var end: T
public var begin: T
public var end: T
init(_ begin: T, _ end: T) {
public init(_ begin: T, _ end: T) {
self.begin = begin
self.end = end

View File

@ -1,5 +1,6 @@
//
// String+Case.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -8,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2018-2022 1024jp
// © 2018-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -25,7 +26,7 @@
import Foundation.NSRegularExpression
extension String {
public extension String {
/// Transform all camel and pascal case words to snake case.
var snakecased: String {
@ -66,7 +67,7 @@ extension String {
private func ranges(pattern: String) -> [Range<Index>] {
(try! NSRegularExpression(pattern: pattern))
.matches(in: self, range: self.nsRange)
.matches(in: self, range: NSRange(..<self.utf16.count))
.map(\.range)
.map { String.Index(utf16Offset: $0.lowerBound, in: self)..<String.Index(utf16Offset: $0.upperBound, in: self) }
}

View File

@ -0,0 +1,172 @@
//
// String+Counting.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2014-05-04.
//
// ---------------------------------------------------------------------------
//
// © 2014-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
public extension StringProtocol {
/// The number of words in the whole string.
var numberOfWords: Int {
var count = 0
self.enumerateSubstrings(in: self.startIndex..<self.endIndex, options: [.byWords, .localized, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
return count
}
/// The number of lines in the whole string excluding the last blank line.
var numberOfLines: Int {
self.numberOfLines()
}
/// Calculates the line number at the given character index (1-based).
///
/// - Parameter index: The character index.
/// - Returns: The line number.
func lineNumber(at index: Index) -> Int {
guard !self.isEmpty, index > self.startIndex else { return 1 }
return self.numberOfLines(in: self.startIndex..<index, includesLastBreak: true)
}
/// Counts the number of lines in the given range.
///
/// - Parameters:
/// - range: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: Range<String.Index>? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.startIndex..<self.endIndex
if self.isEmpty || range.isEmpty { return 0 }
var count = 0
self.enumerateSubstrings(in: range, options: [.byLines, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
if includesLastBreak, self[range].last?.isNewline == true {
count += 1
}
return count
}
/// Counts the number of lines in the given ranges.
///
/// - Parameters:
/// - ranges: The character ranges to count lines.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in ranges: [Range<String.Index>], includesLastBreak: Bool = false) -> Int {
assert(!ranges.isEmpty)
if self.isEmpty || ranges.isEmpty { return 0 }
// use simple count for efficiency
if ranges.count == 1 {
return self.numberOfLines(in: ranges[0], includesLastBreak: includesLastBreak)
}
// evaluate line ranges to avoid double-count lines holding multiple ranges
var lineRanges: [Range<String.Index>] = []
for range in ranges {
let lineRange = self.lineRange(for: range)
self.enumerateSubstrings(in: lineRange, options: [.byLines, .substringNotRequired]) { (_, substringRange, _, _) in
lineRanges.append(substringRange)
}
if includesLastBreak, self[range].last?.isNewline == true {
lineRanges.append(self.lineRange(at: range.upperBound))
}
}
return lineRanges.uniqued.count
}
/// Calculates the number of characters from the beginning of the line where the given character index locates (0-based).
///
/// - Parameter index: The character index.
/// - Returns: The column number.
func columnNumber(at index: Index) -> Int {
self.distance(from: self.lineStartIndex(at: index), to: index)
}
}
// MARK: NSRange based
public extension String {
/// Calculates the line number at the given character index (1-based).
///
/// - Parameter location: The UTF16-based character index.
/// - Returns: The line number.
func lineNumber(at location: Int) -> Int {
guard !self.isEmpty, location > 0 else { return 1 }
return self.numberOfLines(in: NSRange(location: 0, length: location), includesLastBreak: true)
}
/// Counts the number of lines in the given range.
///
/// - Parameters:
/// - ranges: The character range to count lines, or when `nil`, the entire range.
/// - includesLastBreak: The flag to count the new line character at the end.
/// - Returns: The number of lines.
func numberOfLines(in range: NSRange? = nil, includesLastBreak: Bool = false) -> Int {
let range = range ?? self.nsRange
if self.isEmpty || range.isEmpty { return 0 }
var count = 0
(self as NSString).enumerateSubstrings(in: range, options: [.byLines, .substringNotRequired]) { (_, _, _, _) in
count += 1
}
if includesLastBreak, (self as NSString).character(at: range.upperBound - 1).isNewline == true {
count += 1
}
return count
}
}

View File

@ -1,5 +1,6 @@
//
// String+Escaping.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
extension String {
public extension String {
/// Unescaped version of the string by unescaping the characters with backslashes.
var unescaped: String {
@ -49,7 +50,7 @@ extension String {
private let maxEscapesCheckLength = 8
extension StringProtocol {
public extension StringProtocol {
/// Checks if character at the index is escaped with backslash.
///

View File

@ -1,5 +1,6 @@
//
// String+FullwidthTransform.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -8,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2014-2020 1024jp
// © 2014-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,7 +24,7 @@
// limitations under the License.
//
extension StringProtocol {
public extension StringProtocol {
/// Transforms half-width roman characters to full-width forms, or vice versa.
///

View File

@ -1,5 +1,6 @@
//
// String+Indentation.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
enum IndentStyle {
public enum IndentStyle: Equatable, Sendable {
case tab
case space
@ -39,7 +40,7 @@ private enum DetectionLines {
}
extension String {
public extension String {
/// Increases indent level in.
func indent(style: IndentStyle, indentWidth: Int, in selectedRanges: [NSRange]) -> EditingContext {
@ -62,8 +63,8 @@ extension String {
// calculate new selection range
let newSelectedRanges = selectedRanges.map { selectedRange -> NSRange in
let shift = lineRanges.countPrefix { $0.location <= selectedRange.location }
let lineCount = lineRanges.count { selectedRange.intersects($0) }
let shift = lineRanges.prefix(while: { $0.location <= selectedRange.location }).count
let lineCount = lineRanges.prefix(while: { selectedRange.intersects($0) }).count
let lengthDiff = max(lineCount - 1, 0) * indentLength
return NSRange(location: selectedRange.location + shift * indentLength,
@ -88,7 +89,7 @@ extension String {
let dropCounts = lines.map { line -> Int in
switch line.first {
case "\t": 1
case " ": line.prefix(indentWidth).countPrefix { $0 == " " }
case " ": line.prefix(indentWidth).prefix(while: { $0 == " " }).count
default: 0
}
}
@ -148,9 +149,7 @@ extension String {
}
extension String {
// MARK: Public Methods
public extension String {
/// Detected indent style.
var detectedIndentStyle: IndentStyle? {

View File

@ -1,5 +1,6 @@
//
// String+LineProcessing.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
extension String {
public extension String {
/// Moves selected line up.
func moveLineUp(in ranges: [NSRange]) -> EditingContext? {
@ -250,7 +251,7 @@ extension String {
}
extension String {
public extension String {
/// Sorts selected lines ascending.
func sortLinesAscending(in range: NSRange) -> EditingContext? {
@ -302,7 +303,7 @@ extension String {
}
extension String {
public extension String {
/// Trims all trailing whitespace with/without keeping editing point.
func trimTrailingWhitespace(ignoringEmptyLines: Bool, keepingEditingPoint: Bool = false, in editingRanges: [NSRange]) -> EditingContext? {

View File

@ -1,5 +1,6 @@
//
// String+LineRange.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
extension String {
public extension String {
/// The first appeared line ending character.
var firstLineEnding: Character? {
@ -35,7 +36,7 @@ extension String {
}
extension StringProtocol {
public extension StringProtocol {
/// Returns the range of the line containing a given index.
///
@ -112,7 +113,7 @@ extension StringProtocol {
// MARK: NSRange based
extension String {
public extension String {
/// Divides the given range into logical line contents ranges.
///
@ -120,7 +121,7 @@ extension String {
/// - Returns: Logical line ranges.
func lineContentsRanges(for range: NSRange? = nil) -> [NSRange] {
let range = range ?? self.nsRange
let range = range ?? NSRange(..<self.utf16.count)
let regex = try! NSRegularExpression(pattern: "^.*", options: [.anchorsMatchLines])
return regex.matches(in: self, range: range).map(\.range)

View File

@ -1,5 +1,6 @@
//
// String+Match.swift
// TextEditing
//
// CotEditor
// https://coteditor.com
@ -25,21 +26,24 @@
import Foundation
struct FilteredItem<Value: Identifiable & Sendable>: Identifiable, Sendable {
public struct FilteredItem<Value: Identifiable & Sendable>: Identifiable, Sendable {
enum State {
public enum State: Sendable {
case noFilter
case filtered([Range<String.Index>])
}
var value: Value
var state: State
var string: String
public var value: Value
public var state: State
public var string: String
var id: Value.ID { self.value.id }
public var id: Value.ID { self.value.id }
}
public extension FilteredItem {
/// Attributed string of which matched parts are styled as `.inlinePresentationIntent = .stronglyEmphasized`.
var attributedString: AttributedString {
@ -62,7 +66,7 @@ struct FilteredItem<Value: Identifiable & Sendable>: Identifiable, Sendable {
}
extension Identifiable where Self: Sendable {
public extension Identifiable where Self: Sendable {
/// Filters with given string.
///
@ -83,13 +87,13 @@ extension Identifiable where Self: Sendable {
}
extension String {
public extension String {
struct AbbreviatedMatchResult {
struct AbbreviatedMatchResult: Equatable, Sendable {
var ranges: [Range<String.Index>]
var remaining: String
var score: Int
public var ranges: [Range<String.Index>]
public var remaining: String
public var score: Int
}

View File

@ -1,6 +1,6 @@
//
// BracePairTests.swift
// Tests
// TextEditingTests
//
// CotEditor
// https://coteditor.com
@ -25,7 +25,7 @@
//
import Testing
@testable import CotEditor
@testable import TextEditing
struct BracePairTests {

View File

@ -0,0 +1,70 @@
//
// LineRangeTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2024-06-19.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import Testing
@testable import TextEditing
struct LineRangeTests {
@Test func lineRange() {
let string = "foo\n\rbar\n\r"
#expect(string.lineContentsRange(for: string.startIndex..<string.endIndex) ==
string.startIndex..<string.index(before: string.endIndex))
#expect(string.lineRange(at: string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 4))
#expect(string.lineContentsRange(for: string.startIndex..<string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 3))
#expect((string as NSString).lineContentsRange(for: NSRange(..<1)) == NSRange(..<3))
#expect((string as NSString).lineContentsRange(at: 5) == NSRange(5..<8))
let emptyString = ""
let emptyRange = emptyString.startIndex..<emptyString.endIndex
#expect(emptyString.lineContentsRange(for: emptyRange) == emptyRange)
}
@Test func lineRanges() {
#expect("foo\nbar".lineContentsRanges(for: NSRange(1..<1)) == [NSRange(1..<1)])
#expect("foo\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\nbar\n".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\r\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(5..<8)])
#expect("foo\r\r\rbar".lineContentsRanges().count == 4)
}
@Test func firstLineEnding() {
#expect("foo\r\nbar".firstLineEnding == "\r\n")
}
}

View File

@ -1,5 +1,6 @@
//
// NSRangeTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
import Testing
@testable import CotEditor
@testable import TextEditing
struct NSRangeTests {

View File

@ -0,0 +1,119 @@
//
// NSStringTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2024-06-19.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import Testing
@testable import TextEditing
struct NSStringTests {
/// Tests if the U+FEFF omitting bug on Swift 5 still exists.
@Test(.bug("https://bugs.swift.org/browse/SR-10896")) func immutable() {
#expect("abc".immutable == "abc")
let bom = "\u{feff}"
let string = "\(bom)abc"
withKnownIssue {
#expect(string.immutable == string)
}
}
@Test func beforeIndex() {
#expect(("00" as NSString).index(before: 0) == 0)
#expect(("00" as NSString).index(before: 1) == 0)
#expect(("00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 1) == 0)
#expect(("0🇦🇦00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 5) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 6) == 5)
#expect(("0\r\n0" as NSString).index(before: 3) == 1)
#expect(("0\r\n0" as NSString).index(before: 2) == 1)
#expect(("0\r\n0" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 2) == 1)
}
@Test func afterIndex() {
#expect(("00" as NSString).index(after: 0) == 1)
#expect(("00" as NSString).index(after: 1) == 2)
#expect(("00" as NSString).index(after: 2) == 2)
#expect(("0🇦🇦0" as NSString).index(after: 0) == 1)
#expect(("0🇦🇦0" as NSString).index(after: 1) == 5)
#expect(("0\r\n0" as NSString).index(after: 1) == 3)
#expect(("0\r\n0" as NSString).index(after: 2) == 3)
#expect(("0\r\n0" as NSString).index(after: 3) == 4)
#expect(("0\r" as NSString).index(after: 1) == 2)
#expect(("0\r" as NSString).index(after: 2) == 2)
// composed character does not care CRLF
#expect(("\r\n" as NSString).rangeOfComposedCharacterSequence(at: 1) == NSRange(1..<2))
}
@Test func rangeOfCharacter() {
let set = CharacterSet(charactersIn: "._")
let string = "abc.d🐕f_ghij" as NSString
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 0)) == "abc")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 4)) == "d🐕f")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: string.length - 1)) == "ghij")
}
@Test func composedCharacterSequence() {
let blackDog = "🐕‍⬛" // 4
#expect(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1) == 0)
let abcDog = "🐕⬛abc" // 4 1 1 1
#expect(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕⬛a".utf16.count)
#expect(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == "🐕‍⬛".utf16.count)
let dogDog = "🐕‍⬛🐕" // 4 2
#expect(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == 0)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕‍⬛".utf16.count)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0) == "🐕‍⬛🐕".utf16.count)
let string = "🐕🏴‍☠️🇯🇵🧑‍💻" // 2 5 4 5
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1) == "🐕".utf16.count)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0) == "🐕🏴‍☠️".utf16.count)
let abc = "abc"
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0) == 1)
}
}

View File

@ -0,0 +1,119 @@
//
// StringCountingTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2015-11-09.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import Testing
@testable import TextEditing
struct StringCountingTests {
@Test func countComposedCharacters() {
// make sure that `String.count` counts characters as I want
#expect("foo".count == 3)
#expect("\r\n".count == 1)
#expect("😀🇯🇵a".count == 3)
#expect("😀🏻".count == 1)
#expect("👍🏻".count == 1)
// single regional indicator
#expect("🇦 ".count == 2)
}
@Test func countWords() {
#expect("Clarus says moof!".numberOfWords == 3)
#expect("plain-text".numberOfWords == 2)
#expect("!".numberOfWords == 0)
#expect("".numberOfWords == 0)
}
@Test func countLines() {
#expect("".numberOfLines == 0)
#expect("a".numberOfLines == 1)
#expect("\n".numberOfLines == 1)
#expect("\n\n".numberOfLines == 2)
#expect("\u{feff}".numberOfLines == 1)
#expect("ab\r\ncd".numberOfLines == 2)
let testString = "a\nb c\n\n"
#expect(testString.numberOfLines == 3)
#expect(testString.numberOfLines(in: NSRange(0..<0)) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1)) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2)) == 1) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6)) == 2) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7)) == 3) // "a\nb c\n\n"
#expect(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true) == 2) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6), includesLastBreak: true) == 3) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7), includesLastBreak: true) == 4) // "a\nb c\n\n"
#expect(testString.lineNumber(at: 0) == 1)
#expect(testString.lineNumber(at: 1) == 1)
#expect(testString.lineNumber(at: 2) == 2)
#expect(testString.lineNumber(at: 5) == 2)
#expect(testString.lineNumber(at: 6) == 3)
#expect(testString.lineNumber(at: 7) == 4)
let nsString = testString as NSString
#expect(nsString.lineNumber(at: 0) == testString.lineNumber(at: 0))
#expect(nsString.lineNumber(at: 1) == testString.lineNumber(at: 1))
#expect(nsString.lineNumber(at: 2) == testString.lineNumber(at: 2))
#expect(nsString.lineNumber(at: 5) == testString.lineNumber(at: 5))
#expect(nsString.lineNumber(at: 6) == testString.lineNumber(at: 6))
#expect(nsString.lineNumber(at: 7) == testString.lineNumber(at: 7))
#expect("\u{FEFF}".numberOfLines(in: NSRange(0..<1)) == 1) // "\u{FEFF}"
#expect("\u{FEFF}\nb".numberOfLines(in: NSRange(0..<3)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\nb".numberOfLines(in: NSRange(1..<4)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\u{FEFF}\nb".numberOfLines(in: NSRange(1..<5)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}0000000000000000".numberOfLines == 1)
let bomString = "\u{FEFF}\nb"
let range = bomString.startIndex..<bomString.index(bomString.startIndex, offsetBy: 2)
#expect(bomString.numberOfLines(in: [range, range]) == 1) // "\u{FEFF}\n"
}
@Test func countColumns() {
let string = "aaa \r\n🐱 "
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)) == 3)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)) == 4)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)) == 0)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 6)) == 1)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 7)) == 2)
}
}

View File

@ -0,0 +1,118 @@
//
// StringExtensionsTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2024-06-19.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import Testing
@testable import TextEditing
struct StringExtensionsTests {
@Suite struct Escaping {
@Test func escapeCharacter() {
let string = "a\\a\\\\aa"
#expect(!string.isCharacterEscaped(at: 0))
#expect(string.isCharacterEscaped(at: 2))
#expect(!string.isCharacterEscaped(at: 5))
}
@Test func unescape() {
#expect(#"\\"#.unescaped == "\\")
#expect(#"\'"#.unescaped == "\'")
#expect(#"\""#.unescaped == "\"")
#expect(#"a\n "#.unescaped == "a\n ")
#expect(#"a\\n "#.unescaped == "a\\n ")
#expect(#"a\\\n "#.unescaped == "a\\\n ")
#expect(#"a\\\\n"#.unescaped == "a\\\\n")
#expect(#"\\\\\t"#.unescaped == "\\\\\t")
#expect(#"\\foo\\\\\0bar\\"#.unescaped == "\\foo\\\\\u{0}bar\\")
#expect(#"\\\\\\\\foo"#.unescaped == "\\\\\\\\foo")
#expect(#"foo: \r\n1"#.unescaped == "foo: \r\n1")
}
}
@Test func fullwidthRoman() {
let testString = "犬 イヌ いぬ Dog 123 "
#expect(testString.fullwidthRoman(reverse: false) == "犬 イヌ いぬ ")
#expect(testString.fullwidthRoman(reverse: true) == "犬 イヌ いぬ Inu Dog 123 123")
}
@Test func codingCases() {
#expect("AbcDefg Hij".snakecased == "abc_defg hij")
#expect("abcDefg Hij".snakecased == "abc_defg hij")
#expect("_abcDefg Hij".snakecased == "_abc_defg hij")
#expect("AA\u{0308}".snakecased == "a_a\u{0308}")
#expect("abÄb".snakecased == "ab_äb")
#expect("abc_defg Hij".camelcased == "abcDefg hij")
#expect("AbcDefg Hij".camelcased == "abcDefg hij")
#expect("_abcDefg Hij".camelcased == "_abcDefg hij")
#expect("a_a\u{0308}".camelcased == "aA\u{0308}")
#expect("abc_defg Hij".pascalcased == "AbcDefg Hij")
#expect("abcDefg Hij".pascalcased == "AbcDefg Hij")
#expect("_abcDefg Hij".pascalcased == "_abcDefg Hij")
#expect("a_a\u{0308}".pascalcased == "AA\u{0308}")
}
@Test func abbreviatedMatch() throws {
let string = "The fox jumps over the lazy dogcow."
#expect(string.abbreviatedMatch(with: "quick") == nil)
let dogcow = try #require(string.abbreviatedMatch(with: "dogcow"))
#expect(dogcow.score == 6)
#expect(dogcow.ranges.count == 6)
#expect(dogcow.remaining.isEmpty)
let ow = try #require(string.abbreviatedMatch(with: "ow"))
#expect(ow.score == 29)
#expect(ow.ranges.count == 2)
#expect(ow.remaining.isEmpty)
let lazyTanuki = try #require(string.abbreviatedMatch(with: "lazy tanuki"))
#expect(lazyTanuki.score == 5)
#expect(lazyTanuki.ranges.count == 5)
#expect(lazyTanuki.remaining == "tanuki")
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki") == nil)
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count == 5)
#expect(string.abbreviatedMatchedRanges(with: "lazy w")?.count == 6)
#expect(string.abbreviatedMatchedRanges(with: "lazy w", incomplete: true)?.count == 6)
}
}

View File

@ -1,6 +1,6 @@
//
// StringIndentationTests.swift
// Tests
// TextEditingTests
//
// CotEditor
// https://coteditor.com
@ -26,7 +26,7 @@
import Foundation
import Testing
@testable import CotEditor
@testable import TextEditing
struct StringIndentationTests {

View File

@ -1,5 +1,6 @@
//
// StringLineProcessingTests.swift
// TextEditingTests
//
// CotEditor
// https://coteditor.com
@ -25,7 +26,7 @@
import Foundation
import Testing
@testable import CotEditor
@testable import TextEditing
struct StringLineProcessingTests {
@ -245,9 +246,55 @@ struct StringLineProcessingTests {
}
struct StringTrimmingTests {
@Test func trimWhitespace() throws {
let string = """
abc def
\t
white space -> \t
abc
"""
let trimmed = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoringEmptyLines: false))
let expectedTrimmed = """
abc def
white space ->
abc
"""
#expect(trimmed == expectedTrimmed)
let trimmedIgnoringEmptyLines = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoringEmptyLines: true))
let expectedTrimmedIgnoringEmptyLines = """
abc def
\t
white space ->
abc
"""
#expect(trimmedIgnoringEmptyLines == expectedTrimmedIgnoringEmptyLines)
}
}
// MARK: -
private extension String {
func trim(ranges: [NSRange]) throws -> String {
try ranges.reversed()
.map { try #require(Range($0, in: self)) }
.reduce(self) { $0.replacingCharacters(in: $1, with: "") }
}
}
private extension NSRange {
init(_ location: Int, _ length: Int) {

View File

@ -47,11 +47,7 @@ struct CollectionTests {
}
@Test func count() {
#expect([1, 2, 0, -1, 3].count(where: { $0 > 0 }) == 3)
#expect([0, 1, 2, 0, -1].count(where: { $0 > 0 }) == 2)
#expect([1, 2, 3, 4, 5].count(where: { $0 > 0 }) == 5)
@Test func countPrefix() {
#expect([1, 2, 0, -1, 3].countPrefix(while: { $0 > 0 }) == 2)
#expect([0, 1, 2, 0, -1].countPrefix(while: { $0 > 0 }) == 0)

View File

@ -127,17 +127,19 @@ import Testing
counter.invalidateContent()
counter.invalidateSelection()
// #expect(counter.result.lines.entire == 2)
// #expect(counter.result.characters.entire == 3)
// #expect(counter.result.words.entire == 2)
withKnownIssue("values will be updated asynchronously (This is the issue on the test side.)") {
#expect(counter.result.lines.entire == 2)
#expect(counter.result.characters.entire == 3)
#expect(counter.result.words.entire == 2)
// #expect(counter.result.lines.selected == 2)
// #expect(counter.result.characters.selected == 2)
// #expect(counter.result.words.selected == 1)
#expect(counter.result.lines.selected == 2)
#expect(counter.result.characters.selected == 2)
#expect(counter.result.words.selected == 1)
// #expect(counter.result.location == 1)
// #expect(counter.result.column == 1)
// #expect(counter.result.line == 1)
#expect(counter.result.location == 1)
#expect(counter.result.column == 1)
#expect(counter.result.line == 1)
}
}

View File

@ -0,0 +1,40 @@
//
// NSTextStorageTests.swift
//
// CotEditor
// https://coteditor.com
//
// Created by imanishi on 2024/06/19.
//
// ---------------------------------------------------------------------------
//
// © 2024 CotEditor Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import AppKit.NSTextStorage
import Testing
struct NSTextStorageTests {
@Test func bigMutableString() {
let string = NSTextStorage(string: String(repeating: "a", count: 512)).string
let bigString = NSTextStorage(string: String(repeating: "a", count: 513)).string
#expect((string as NSString).className == "__NSCFString")
#expect((bigString as NSString).className == "NSBigMutableString")
#expect(!(bigString.immutable as NSString).className.contains("Mutable"))
}
}

View File

@ -0,0 +1,93 @@
//
// StringAdvancedCountTests.swift
// Tests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2015-11-09.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import Testing
@testable import CotEditor
struct StringAdvancedCountTests {
@Test func countCharactersWithOptions() {
var options = CharacterCountOptions()
let string = "aaa \t 🐱\n\r\n c"
#expect(string.count(options: options) == string.count)
options.ignoresNewlines = true
#expect(string.count(options: options) == 9)
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 5)
options.ignoresNewlines = false
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 7)
// test .treatsConsecutiveWhitespaceAsSingle
options = .init()
options.treatsConsecutiveWhitespaceAsSingle = true
#expect(string.count(options: options) == 7)
options.ignoresNewlines = true
#expect(string.count(options: options) == 7)
options.treatsConsecutiveWhitespaceAsSingle = false
options.ignoresNewlines = true
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 5)
// test other units
options = .init()
options.unit = .unicodeScalar
#expect(string.count(options: options) == 12)
options.unit = .utf16
#expect(string.count(options: options) == 13)
// test normalization
let aUmlaut = ""
options = .init()
options.unit = .unicodeScalar
#expect(aUmlaut.count(options: options) == 2)
options.normalizationForm = .nfc
#expect(aUmlaut.count(options: options) == 1)
}
@Test func countBytes() {
let string = "abc犬牛"
var options = CharacterCountOptions(unit: .byte)
options.encoding = .utf8
#expect(string.count(options: options) == 9)
options.encoding = .shiftJIS
#expect(string.count(options: options) == 7)
options.encoding = .ascii
#expect(string.count(options: options) == nil)
options.encoding = .nonLossyASCII
#expect(string.count(options: options) == 15)
}
}

View File

@ -25,6 +25,7 @@
//
import Foundation
import TextEditing
import Testing
@testable import CotEditor

View File

@ -1,447 +0,0 @@
//
// StringExtensionsTests.swift
// Tests
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2015-11-09.
//
// ---------------------------------------------------------------------------
//
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import AppKit.NSTextStorage
import Testing
@testable import CotEditor
struct StringExtensionsTests {
/// Tests if the U+FEFF omitting bug on Swift 5 still exists.
@Test(.bug("https://bugs.swift.org/browse/SR-10896")) func immutable() {
#expect("abc".immutable == "abc")
let bom = "\u{feff}"
let string = "\(bom)abc"
withKnownIssue {
#expect(string.immutable == string)
}
}
@Test func bigMutableString() {
let string = NSTextStorage(string: String(repeating: "a", count: 512)).string
let bigString = NSTextStorage(string: String(repeating: "a", count: 513)).string
#expect((string as NSString).className == "__NSCFString")
#expect((bigString as NSString).className == "NSBigMutableString")
#expect(!(bigString.immutable as NSString).className.contains("Mutable"))
}
@Test func escapeCharacter() {
let string = "a\\a\\\\aa"
#expect(!string.isCharacterEscaped(at: 0))
#expect(string.isCharacterEscaped(at: 2))
#expect(!string.isCharacterEscaped(at: 5))
}
@Test func unescape() {
#expect(#"\\"#.unescaped == "\\")
#expect(#"\'"#.unescaped == "\'")
#expect(#"\""#.unescaped == "\"")
#expect(#"a\n "#.unescaped == "a\n ")
#expect(#"a\\n "#.unescaped == "a\\n ")
#expect(#"a\\\n "#.unescaped == "a\\\n ")
#expect(#"a\\\\n"#.unescaped == "a\\\\n")
#expect(#"\\\\\t"#.unescaped == "\\\\\t")
#expect(#"\\foo\\\\\0bar\\"#.unescaped == "\\foo\\\\\u{0}bar\\")
#expect(#"\\\\\\\\foo"#.unescaped == "\\\\\\\\foo")
#expect(#"foo: \r\n1"#.unescaped == "foo: \r\n1")
}
@Test func countComposedCharacters() {
// make sure that `String.count` counts characters as I want
#expect("foo".count == 3)
#expect("\r\n".count == 1)
#expect("😀🇯🇵a".count == 3)
#expect("😀🏻".count == 1)
#expect("👍🏻".count == 1)
// single regional indicator
#expect("🇦 ".count == 2)
}
@Test func countWords() {
#expect("Clarus says moof!".numberOfWords == 3)
#expect("plain-text".numberOfWords == 2)
#expect("!".numberOfWords == 0)
#expect("".numberOfWords == 0)
}
@Test func countLines() {
#expect("".numberOfLines == 0)
#expect("a".numberOfLines == 1)
#expect("\n".numberOfLines == 1)
#expect("\n\n".numberOfLines == 2)
#expect("\u{feff}".numberOfLines == 1)
#expect("ab\r\ncd".numberOfLines == 2)
let testString = "a\nb c\n\n"
#expect(testString.numberOfLines == 3)
#expect(testString.numberOfLines(in: NSRange(0..<0)) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1)) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2)) == 1) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6)) == 2) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7)) == 3) // "a\nb c\n\n"
#expect(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true) == 2) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6), includesLastBreak: true) == 3) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7), includesLastBreak: true) == 4) // "a\nb c\n\n"
#expect(testString.lineNumber(at: 0) == 1)
#expect(testString.lineNumber(at: 1) == 1)
#expect(testString.lineNumber(at: 2) == 2)
#expect(testString.lineNumber(at: 5) == 2)
#expect(testString.lineNumber(at: 6) == 3)
#expect(testString.lineNumber(at: 7) == 4)
let nsString = testString as NSString
#expect(nsString.lineNumber(at: 0) == testString.lineNumber(at: 0))
#expect(nsString.lineNumber(at: 1) == testString.lineNumber(at: 1))
#expect(nsString.lineNumber(at: 2) == testString.lineNumber(at: 2))
#expect(nsString.lineNumber(at: 5) == testString.lineNumber(at: 5))
#expect(nsString.lineNumber(at: 6) == testString.lineNumber(at: 6))
#expect(nsString.lineNumber(at: 7) == testString.lineNumber(at: 7))
#expect("\u{FEFF}".numberOfLines(in: NSRange(0..<1)) == 1) // "\u{FEFF}"
#expect("\u{FEFF}\nb".numberOfLines(in: NSRange(0..<3)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\nb".numberOfLines(in: NSRange(1..<4)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\u{FEFF}\nb".numberOfLines(in: NSRange(1..<5)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}0000000000000000".numberOfLines == 1)
let bomString = "\u{FEFF}\nb"
let range = bomString.startIndex..<bomString.index(bomString.startIndex, offsetBy: 2)
#expect(bomString.numberOfLines(in: [range, range]) == 1) // "\u{FEFF}\n"
}
@Test func countColumns() {
let string = "aaa \r\n🐱 "
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)) == 3)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)) == 4)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)) == 0)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 6)) == 1)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 7)) == 2)
}
@Test func countCharactersWithOptions() {
var options = CharacterCountOptions()
let string = "aaa \t 🐱\n\r\n c"
#expect(string.count(options: options) == string.count)
options.ignoresNewlines = true
#expect(string.count(options: options) == 9)
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 5)
options.ignoresNewlines = false
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 7)
// test .treatsConsecutiveWhitespaceAsSingle
options = .init()
options.treatsConsecutiveWhitespaceAsSingle = true
#expect(string.count(options: options) == 7)
options.ignoresNewlines = true
#expect(string.count(options: options) == 7)
options.treatsConsecutiveWhitespaceAsSingle = false
options.ignoresNewlines = true
options.ignoresWhitespaces = true
#expect(string.count(options: options) == 5)
// test other units
options = .init()
options.unit = .unicodeScalar
#expect(string.count(options: options) == 12)
options.unit = .utf16
#expect(string.count(options: options) == 13)
// test normalization
let aUmlaut = ""
options = .init()
options.unit = .unicodeScalar
#expect(aUmlaut.count(options: options) == 2)
options.normalizationForm = .nfc
#expect(aUmlaut.count(options: options) == 1)
}
@Test func countBytes() {
let string = "abc犬牛"
var options = CharacterCountOptions(unit: .byte)
options.encoding = .utf8
#expect(string.count(options: options) == 9)
options.encoding = .shiftJIS
#expect(string.count(options: options) == 7)
options.encoding = .ascii
#expect(string.count(options: options) == nil)
options.encoding = .nonLossyASCII
#expect(string.count(options: options) == 15)
}
@Test func codingCases() {
#expect("AbcDefg Hij".snakecased == "abc_defg hij")
#expect("abcDefg Hij".snakecased == "abc_defg hij")
#expect("_abcDefg Hij".snakecased == "_abc_defg hij")
#expect("AA\u{0308}".snakecased == "a_a\u{0308}")
#expect("abÄb".snakecased == "ab_äb")
#expect("abc_defg Hij".camelcased == "abcDefg hij")
#expect("AbcDefg Hij".camelcased == "abcDefg hij")
#expect("_abcDefg Hij".camelcased == "_abcDefg hij")
#expect("a_a\u{0308}".camelcased == "aA\u{0308}")
#expect("abc_defg Hij".pascalcased == "AbcDefg Hij")
#expect("abcDefg Hij".pascalcased == "AbcDefg Hij")
#expect("_abcDefg Hij".pascalcased == "_abcDefg Hij")
#expect("a_a\u{0308}".pascalcased == "AA\u{0308}")
}
@Test func japaneseTransform() {
let testString = "犬 イヌ いぬ Dog 123 "
#expect(testString.fullwidthRoman(reverse: false) == "犬 イヌ いぬ ")
#expect(testString.fullwidthRoman(reverse: true) == "犬 イヌ いぬ Inu Dog 123 123")
}
@Test func beforeIndex() {
#expect(("00" as NSString).index(before: 0) == 0)
#expect(("00" as NSString).index(before: 1) == 0)
#expect(("00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 1) == 0)
#expect(("0🇦🇦00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 5) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 6) == 5)
#expect(("0\r\n0" as NSString).index(before: 3) == 1)
#expect(("0\r\n0" as NSString).index(before: 2) == 1)
#expect(("0\r\n0" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 2) == 1)
}
@Test func afterIndex() {
#expect(("00" as NSString).index(after: 0) == 1)
#expect(("00" as NSString).index(after: 1) == 2)
#expect(("00" as NSString).index(after: 2) == 2)
#expect(("0🇦🇦0" as NSString).index(after: 0) == 1)
#expect(("0🇦🇦0" as NSString).index(after: 1) == 5)
#expect(("0\r\n0" as NSString).index(after: 1) == 3)
#expect(("0\r\n0" as NSString).index(after: 2) == 3)
#expect(("0\r\n0" as NSString).index(after: 3) == 4)
#expect(("0\r" as NSString).index(after: 1) == 2)
#expect(("0\r" as NSString).index(after: 2) == 2)
// composed character does not care CRLF
#expect(("\r\n" as NSString).rangeOfComposedCharacterSequence(at: 1) == NSRange(1..<2))
}
@Test func lineRange() {
let string = "foo\n\rbar\n\r"
#expect(string.lineContentsRange(for: string.startIndex..<string.endIndex) ==
string.startIndex..<string.index(before: string.endIndex))
#expect(string.lineRange(at: string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 4))
#expect(string.lineContentsRange(for: string.startIndex..<string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 3))
#expect((string as NSString).lineContentsRange(for: NSRange(..<1)) == NSRange(..<3))
#expect((string as NSString).lineContentsRange(at: 5) == NSRange(5..<8))
let emptyString = ""
let emptyRange = emptyString.startIndex..<emptyString.endIndex
#expect(emptyString.lineContentsRange(for: emptyRange) == emptyRange)
}
@Test func lineRanges() {
#expect("foo\nbar".lineContentsRanges(for: NSRange(1..<1)) == [NSRange(1..<1)])
#expect("foo\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\nbar\n".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\r\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(5..<8)])
#expect("foo\r\r\rbar".lineContentsRanges().count == 4)
}
@Test func firstLineEnding() {
#expect("foo\r\nbar".firstLineEnding == "\r\n")
}
@Test func rangeOfCharacter() {
let set = CharacterSet(charactersIn: "._")
let string = "abc.d🐕f_ghij" as NSString
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 0)) == "abc")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 4)) == "d🐕f")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: string.length - 1)) == "ghij")
}
@Test func composedCharacterSequence() {
let blackDog = "🐕‍⬛" // 4
#expect(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1) == 0)
let abcDog = "🐕⬛abc" // 4 1 1 1
#expect(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕⬛a".utf16.count)
#expect(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == "🐕‍⬛".utf16.count)
let dogDog = "🐕‍⬛🐕" // 4 2
#expect(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == 0)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕‍⬛".utf16.count)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0) == "🐕‍⬛🐕".utf16.count)
let string = "🐕🏴‍☠️🇯🇵🧑‍💻" // 2 5 4 5
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1) == "🐕".utf16.count)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0) == "🐕🏴‍☠️".utf16.count)
let abc = "abc"
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0) == 1)
}
@Test func trimWhitespace() throws {
let string = """
abc def
\t
white space -> \t
abc
"""
let trimmed = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoringEmptyLines: false))
let expectedTrimmed = """
abc def
white space ->
abc
"""
#expect(trimmed == expectedTrimmed)
let trimmedIgnoringEmptyLines = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoringEmptyLines: true))
let expectedTrimmedIgnoringEmptyLines = """
abc def
\t
white space ->
abc
"""
#expect(trimmedIgnoringEmptyLines == expectedTrimmedIgnoringEmptyLines)
}
@Test func abbreviatedMatch() throws {
let string = "The fox jumps over the lazy dogcow."
#expect(string.abbreviatedMatch(with: "quick") == nil)
let dogcow = try #require(string.abbreviatedMatch(with: "dogcow"))
#expect(dogcow.score == 6)
#expect(dogcow.ranges.count == 6)
#expect(dogcow.remaining.isEmpty)
let ow = try #require(string.abbreviatedMatch(with: "ow"))
#expect(ow.score == 29)
#expect(ow.ranges.count == 2)
#expect(ow.remaining.isEmpty)
let lazyTanuki = try #require(string.abbreviatedMatch(with: "lazy tanuki"))
#expect(lazyTanuki.score == 5)
#expect(lazyTanuki.ranges.count == 5)
#expect(lazyTanuki.remaining == "tanuki")
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki") == nil)
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count == 5)
#expect(string.abbreviatedMatchedRanges(with: "lazy w")?.count == 6)
#expect(string.abbreviatedMatchedRanges(with: "lazy w", incomplete: true)?.count == 6)
}
}
private extension String {
func trim(ranges: [NSRange]) throws -> String {
try ranges.reversed()
.map { try #require(Range($0, in: self)) }
.reduce(self) { $0.replacingCharacters(in: $1, with: "") }
}
}

View File

@ -1,5 +1,5 @@
//
// StringCollectionTests.swift
// StringFilename.swift
// Tests
//
// CotEditor
@ -27,7 +27,7 @@
import Testing
@testable import CotEditor
struct StringCollectionTests {
struct StringFilename {
@Test func createAvailableNames() {

View File

@ -28,6 +28,7 @@ import AppKit.NSTextStorage
import Testing
import Combine
import Yams
import TextEditing
@testable import CotEditor
actor SyntaxTests {