mirror of
https://github.com/coteditor/CotEditor.git
synced 2024-09-11 11:25:57 +03:00
Migrate UnicodeCharacter to Swift (#570)
This commit is contained in:
parent
ae17f6bc84
commit
aa675bfbd5
@ -16,6 +16,7 @@ develop
|
||||
- Drop support for __OS X Mountain Lion__ and __Mavericks__.
|
||||
- Change the regular expression engine for find panel from the Onigmo to the ICU library.
|
||||
- Update line-up of the search options.
|
||||
- Inserting single surrogate character is no more valid.
|
||||
- Remove “Not writable” alert which displayed on file opening.
|
||||
- New acknowledgements window.
|
||||
- Add help buttons to syntax style editor.
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11129.15" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11173.2" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11129.15"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11173.2"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" customClass="UnicodeInputPanelController" customModule="CotEditor" customModuleProvider="target">
|
||||
@ -30,7 +30,7 @@
|
||||
</allowedInputSourceLocales>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="unicode" id="wVY-6I-8j7">
|
||||
<binding destination="-2" name="value" keyPath="codePoint" id="Lk1-2Z-Zx5">
|
||||
<dictionary key="options">
|
||||
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
|
||||
<string key="NSNullPlaceholder">U+1F600</string>
|
||||
@ -65,16 +65,16 @@ DQ
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="character.name" id="rz0-Df-N9T">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Invalid code</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="-2" name="hidden" keyPath="unicode" id="Jvl-4K-DRa">
|
||||
<binding destination="-2" name="hidden" keyPath="codePoint" id="efx-De-iSm">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSIsNil</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="-2" name="value" keyPath="unicodeName" id="3kK-Ug-bH8">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Invalid code</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iYt-Yd-KXc">
|
||||
@ -85,7 +85,7 @@ DQ
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="character.string" id="HAg-qi-yxV">
|
||||
<binding destination="-2" name="value" keyPath="characterString" id="PG0-CO-irt">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">⬚</string>
|
||||
</dictionary>
|
||||
|
@ -284,6 +284,12 @@
|
||||
2A712CA218EFB716001FEC33 /* AutoTabExpand_Off.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2A712CA018EFB716001FEC33 /* AutoTabExpand_Off.tiff */; };
|
||||
2A712CA318EFB716001FEC33 /* AutoTabExpand_On.tiff in Resources */ = {isa = PBXBuildFile; fileRef = 2A712CA118EFB716001FEC33 /* AutoTabExpand_On.tiff */; };
|
||||
2A7135831CFFDC6600ADA555 /* FormattersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A7135821CFFDC6600ADA555 /* FormattersTests.swift */; };
|
||||
2A73B5B41D4675350025337F /* UnicodeCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5B21D4675350025337F /* UnicodeCharacter.swift */; };
|
||||
2A73B5B51D4675350025337F /* UnicodeCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5B21D4675350025337F /* UnicodeCharacter.swift */; };
|
||||
2A73B5B61D4675350025337F /* UnicodeScalar+ControlCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5B31D4675350025337F /* UnicodeScalar+ControlCharacter.swift */; };
|
||||
2A73B5B71D4675350025337F /* UnicodeScalar+ControlCharacter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5B31D4675350025337F /* UnicodeScalar+ControlCharacter.swift */; };
|
||||
2A73B5BC1D468DD30025337F /* UnicodeScalar+Information.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5BB1D468DD30025337F /* UnicodeScalar+Information.swift */; };
|
||||
2A73B5BD1D468DD30025337F /* UnicodeScalar+Information.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A73B5BB1D468DD30025337F /* UnicodeScalar+Information.swift */; };
|
||||
2A75ACCB19E86DDB00444894 /* CotEditor.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 2A75ACCA19E86DDB00444894 /* CotEditor.sdef */; };
|
||||
2A78BF9F1D1AF82A00A583D2 /* IntegrationPaneController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A78BF9E1D1AF82A00A583D2 /* IntegrationPaneController.swift */; };
|
||||
2A78BFA01D1AF82A00A583D2 /* IntegrationPaneController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A78BF9E1D1AF82A00A583D2 /* IntegrationPaneController.swift */; };
|
||||
@ -353,8 +359,6 @@
|
||||
2AA2E0101BFDE0190087BDD6 /* CharacterInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA2E00F1BFDE0190087BDD6 /* CharacterInfoTests.swift */; };
|
||||
2AA2E0131BFE12620087BDD6 /* Unicode.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2AA2E0111BFE12620087BDD6 /* Unicode.strings */; };
|
||||
2AA2E0141BFE12620087BDD6 /* Unicode.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2AA2E0111BFE12620087BDD6 /* Unicode.strings */; };
|
||||
2AA2E01A1BFF9AEE0087BDD6 /* CEUnicodeCharacter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AA2E0191BFF9AEE0087BDD6 /* CEUnicodeCharacter.m */; };
|
||||
2AA2E01B1BFF9AEE0087BDD6 /* CEUnicodeCharacter.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AA2E0191BFF9AEE0087BDD6 /* CEUnicodeCharacter.m */; };
|
||||
2AA2E0261C0454730087BDD6 /* StringIndentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA2E0251C0454730087BDD6 /* StringIndentationTests.swift */; };
|
||||
2AA375441D403F100080C27C /* String+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A4D69261D3FF61C00FBBD0B /* String+Encoding.swift */; };
|
||||
2AA375451D403F110080C27C /* String+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A4D69261D3FF61C00FBBD0B /* String+Encoding.swift */; };
|
||||
@ -447,8 +451,6 @@
|
||||
2AD69B861D3E42F700FBD998 /* TextSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD69B841D3E42F700FBD998 /* TextSelection.swift */; };
|
||||
2AD69B881D3E4FCD00FBD998 /* EditorWrapper+Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD69B871D3E4FCD00FBD998 /* EditorWrapper+Editor.swift */; };
|
||||
2AD69B891D3E4FCD00FBD998 /* EditorWrapper+Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD69B871D3E4FCD00FBD998 /* EditorWrapper+Editor.swift */; };
|
||||
2AD789D51C31AD81008B746F /* CEControlCharacerNames.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD789D41C31AD81008B746F /* CEControlCharacerNames.m */; };
|
||||
2AD789D61C31AD81008B746F /* CEControlCharacerNames.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AD789D41C31AD81008B746F /* CEControlCharacerNames.m */; };
|
||||
2AD7B9AF1D3E832E00E5D6D7 /* DocumentAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7B9AE1D3E832E00E5D6D7 /* DocumentAnalyzer.swift */; };
|
||||
2AD7B9B01D3E832E00E5D6D7 /* DocumentAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7B9AE1D3E832E00E5D6D7 /* DocumentAnalyzer.swift */; };
|
||||
2AD7B9B51D3E9BD100E5D6D7 /* EditorWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7B9B41D3E9BD100E5D6D7 /* EditorWrapper.swift */; };
|
||||
@ -728,6 +730,9 @@
|
||||
2A712CA018EFB716001FEC33 /* AutoTabExpand_Off.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = AutoTabExpand_Off.tiff; sourceTree = "<group>"; };
|
||||
2A712CA118EFB716001FEC33 /* AutoTabExpand_On.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = AutoTabExpand_On.tiff; sourceTree = "<group>"; };
|
||||
2A7135821CFFDC6600ADA555 /* FormattersTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormattersTests.swift; sourceTree = "<group>"; };
|
||||
2A73B5B21D4675350025337F /* UnicodeCharacter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnicodeCharacter.swift; sourceTree = "<group>"; };
|
||||
2A73B5B31D4675350025337F /* UnicodeScalar+ControlCharacter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+ControlCharacter.swift"; sourceTree = "<group>"; };
|
||||
2A73B5BB1D468DD30025337F /* UnicodeScalar+Information.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+Information.swift"; sourceTree = "<group>"; };
|
||||
2A75ACCA19E86DDB00444894 /* CotEditor.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CotEditor.sdef; sourceTree = "<group>"; };
|
||||
2A7846DA18FE035E006BDF00 /* Themes */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Themes; sourceTree = "<group>"; };
|
||||
2A78BF9E1D1AF82A00A583D2 /* IntegrationPaneController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegrationPaneController.swift; sourceTree = "<group>"; };
|
||||
@ -804,8 +809,6 @@
|
||||
2AA2E0121BFE12620087BDD6 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Unicode.strings; sourceTree = "<group>"; };
|
||||
2AA2E0151BFE14310087BDD6 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Unicode.strings"; sourceTree = "<group>"; };
|
||||
2AA2E0161BFE14320087BDD6 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Unicode.strings; sourceTree = "<group>"; };
|
||||
2AA2E0181BFF9AEE0087BDD6 /* CEUnicodeCharacter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CEUnicodeCharacter.h; sourceTree = "<group>"; };
|
||||
2AA2E0191BFF9AEE0087BDD6 /* CEUnicodeCharacter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CEUnicodeCharacter.m; 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>"; };
|
||||
@ -932,8 +935,6 @@
|
||||
2AD616CE1D3E67160016EFB6 /* ODBEditorSuite.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ODBEditorSuite.swift; sourceTree = "<group>"; };
|
||||
2AD69B841D3E42F700FBD998 /* TextSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextSelection.swift; sourceTree = "<group>"; };
|
||||
2AD69B871D3E4FCD00FBD998 /* EditorWrapper+Editor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EditorWrapper+Editor.swift"; sourceTree = "<group>"; };
|
||||
2AD789D31C31AD81008B746F /* CEControlCharacerNames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CEControlCharacerNames.h; sourceTree = "<group>"; };
|
||||
2AD789D41C31AD81008B746F /* CEControlCharacerNames.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CEControlCharacerNames.m; sourceTree = "<group>"; };
|
||||
2AD7B9AE1D3E832E00E5D6D7 /* DocumentAnalyzer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentAnalyzer.swift; sourceTree = "<group>"; };
|
||||
2AD7B9B41D3E9BD100E5D6D7 /* EditorWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorWrapper.swift; sourceTree = "<group>"; };
|
||||
2AD9039A1BCE86A3004F2B8A /* SpellCheck.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = SpellCheck.tiff; sourceTree = "<group>"; };
|
||||
@ -1565,10 +1566,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2AF073FA1D34587500770BA6 /* CharacterInfo.swift */,
|
||||
2AA2E0181BFF9AEE0087BDD6 /* CEUnicodeCharacter.h */,
|
||||
2AA2E0191BFF9AEE0087BDD6 /* CEUnicodeCharacter.m */,
|
||||
2AD789D31C31AD81008B746F /* CEControlCharacerNames.h */,
|
||||
2AD789D41C31AD81008B746F /* CEControlCharacerNames.m */,
|
||||
2A73B5B21D4675350025337F /* UnicodeCharacter.swift */,
|
||||
2A73B5BB1D468DD30025337F /* UnicodeScalar+Information.swift */,
|
||||
2A73B5B31D4675350025337F /* UnicodeScalar+ControlCharacter.swift */,
|
||||
);
|
||||
name = Character;
|
||||
sourceTree = "<group>";
|
||||
@ -2285,6 +2285,7 @@
|
||||
2A4CCBB51D45173000294067 /* EditorTextView+LineProcessing.swift in Sources */,
|
||||
2AF073E71D33CB6F00770BA6 /* ThemeDictionary.swift in Sources */,
|
||||
2AD69B861D3E42F700FBD998 /* TextSelection.swift in Sources */,
|
||||
2A73B5B51D4675350025337F /* UnicodeCharacter.swift in Sources */,
|
||||
2A4257B11D22FD490086DAAD /* ColorCodePanelController.swift in Sources */,
|
||||
2A5D13331D1FB90300D38E6A /* FindPanelTextView.swift in Sources */,
|
||||
2AA45A521D2E938500A1A401 /* NavigationBarController.swift in Sources */,
|
||||
@ -2298,6 +2299,7 @@
|
||||
2A5D13141D1EE8FF00D38E6A /* HUDController.swift in Sources */,
|
||||
2AA7613B1D457BD500031AAF /* String+Indentation.swift in Sources */,
|
||||
2A78BFAB1D1B0E8B00A583D2 /* PreferencesWindowController.swift in Sources */,
|
||||
2A73B5BD1D468DD30025337F /* UnicodeScalar+Information.swift in Sources */,
|
||||
2AF073F51D34076A00770BA6 /* CharacterPopoverController.swift in Sources */,
|
||||
2A8C33901D3E1C040005B0B7 /* IncompatibleCharacter.swift in Sources */,
|
||||
2ACDC08C1D171F1E009B72D6 /* BackgroundView.swift in Sources */,
|
||||
@ -2344,6 +2346,7 @@
|
||||
2A38FAFE1D1C67050032231A /* DraggableArrayController.swift in Sources */,
|
||||
2A4257BD1D239F850086DAAD /* Invisible.swift in Sources */,
|
||||
2A2792961D1DBDAC00F3FC5D /* Constants.swift in Sources */,
|
||||
2A73B5B71D4675350025337F /* UnicodeScalar+ControlCharacter.swift in Sources */,
|
||||
2AAD61F11D2B0856008FE772 /* String+Range.swift in Sources */,
|
||||
2AF073EC1D33F17100770BA6 /* DictionaryKey.swift in Sources */,
|
||||
2A5D13231D1F9B7F00D38E6A /* PopUpToolbarItem.swift in Sources */,
|
||||
@ -2429,8 +2432,6 @@
|
||||
2AD49C291D0BEF3800FF4CC9 /* CESettingFileManager.m in Sources */,
|
||||
2A6FD9FA1D3B05F100A59784 /* CESyntaxManager.m in Sources */,
|
||||
2AB340801D01E2A4000F5EEC /* CESyntaxDictionaryKeys.m in Sources */,
|
||||
2AA2E01B1BFF9AEE0087BDD6 /* CEUnicodeCharacter.m in Sources */,
|
||||
2AD789D61C31AD81008B746F /* CEControlCharacerNames.m in Sources */,
|
||||
2A41CB411C39167500F9122B /* CEDefaults.m in Sources */,
|
||||
2A41CB471C391B7400F9122B /* CEErrors.m in Sources */,
|
||||
2A6F0DA51B5500E100C2D03C /* Constants.m in Sources */,
|
||||
@ -2506,6 +2507,7 @@
|
||||
2A6FD9EA1D3A819500A59784 /* EditorTextView+Commenting.swift in Sources */,
|
||||
2A78BFB61D1B2BA600A583D2 /* MigrationWindowController.swift in Sources */,
|
||||
2A33D07E1D1C75B8005977B9 /* SyntaxValidationViewController.swift in Sources */,
|
||||
2A73B5B61D4675350025337F /* UnicodeScalar+ControlCharacter.swift in Sources */,
|
||||
2A6FD9E01D393F9100A59784 /* SplitViewController.swift in Sources */,
|
||||
2A38FAFD1D1C67050032231A /* DraggableArrayController.swift in Sources */,
|
||||
2A6FD9D11D38933100A59784 /* EditorTextViewController.swift in Sources */,
|
||||
@ -2587,6 +2589,7 @@
|
||||
2A5D13251D1F9D4100D38E6A /* TogglableToolbarItem.swift in Sources */,
|
||||
2AA4D3741D1AA0AC001D261D /* KeyBindingsViewController.swift in Sources */,
|
||||
2AACB1CD1D195ABD0073775B /* ShortcutKeyField.swift in Sources */,
|
||||
2A73B5B41D4675350025337F /* UnicodeCharacter.swift in Sources */,
|
||||
2A6FD9ED1D3A85D700A59784 /* String+NSRange.swift in Sources */,
|
||||
2ACFE58B1D20730B0005233A /* FindPanelContentViewController.swift in Sources */,
|
||||
2A78BFA41D1B02ED00A583D2 /* WindowPaneController.swift in Sources */,
|
||||
@ -2598,6 +2601,7 @@
|
||||
2A5D13321D1FB90300D38E6A /* FindPanelTextView.swift in Sources */,
|
||||
2A38FB001D1C6B6D0032231A /* SyntaxEditTableViewDelegate.swift in Sources */,
|
||||
2A5D13351D1FC87900D38E6A /* FindPanelTextClipView.swift in Sources */,
|
||||
2A73B5BC1D468DD30025337F /* UnicodeScalar+Information.swift in Sources */,
|
||||
2AFAFD4A1D41487600F1458F /* PrintTextView.swift in Sources */,
|
||||
2A5D130A1D1ED10400D38E6A /* ConsolePanelController.swift in Sources */,
|
||||
2AA45A511D2E938500A1A401 /* NavigationBarController.swift in Sources */,
|
||||
@ -2614,8 +2618,6 @@
|
||||
2AD49C281D0BEF3800FF4CC9 /* CESettingFileManager.m in Sources */,
|
||||
2A6FD9F91D3B05F100A59784 /* CESyntaxManager.m in Sources */,
|
||||
2AB3407F1D01E2A4000F5EEC /* CESyntaxDictionaryKeys.m in Sources */,
|
||||
2AA2E01A1BFF9AEE0087BDD6 /* CEUnicodeCharacter.m in Sources */,
|
||||
2AD789D51C31AD81008B746F /* CEControlCharacerNames.m in Sources */,
|
||||
2A41CB401C39167500F9122B /* CEDefaults.m in Sources */,
|
||||
2A41CB461C391B7400F9122B /* CEErrors.m in Sources */,
|
||||
2A149DD719033CBB00A9D6EF /* Constants.m in Sources */,
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
|
||||
CEControlCharacerNames.h
|
||||
|
||||
CotEditor
|
||||
http://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-12-29.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 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
|
||||
|
||||
http://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;
|
||||
|
||||
|
||||
BOOL CEIsC0ControlPoint(unichar character);
|
||||
BOOL CEIsC1ControlPoint(unichar character);
|
||||
|
||||
extern unichar const CEDeleteCharacter;
|
||||
|
||||
NSString * _Nullable CEControlCharacterName(unichar character);
|
@ -1,136 +0,0 @@
|
||||
/*
|
||||
|
||||
CEControlCharacerNames.m
|
||||
|
||||
CotEditor
|
||||
http://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-12-29.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 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
|
||||
|
||||
http://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 "CEControlCharacerNames.h"
|
||||
|
||||
|
||||
BOOL CEIsC0ControlPoint(unichar character) {
|
||||
return 0x0000 <= character && character <= 0x0020; // U+0020 is actually not in range of C0 control character. But they are often included in actual fact.
|
||||
}
|
||||
|
||||
|
||||
BOOL CEIsC1ControlPoint(unichar character) {
|
||||
return 0x0080 <= character && character <= 0x009F;
|
||||
}
|
||||
|
||||
|
||||
unichar const CEDeleteCharacter = 0x007F;
|
||||
|
||||
|
||||
// c.f. http://unicode.org/Public/UNIDATA/UnicodeData.txt
|
||||
static NSString *_Nonnull const CEC0ControlCharacterNames[] = {
|
||||
@"NULL",
|
||||
@"START OF HEADING",
|
||||
@"START OF TEXT",
|
||||
@"END OF TEXT",
|
||||
@"END OF TRANSMISSION",
|
||||
@"ENQUIRY",
|
||||
@"ACKNOWLEDGE",
|
||||
@"BELL",
|
||||
@"BACKSPACE",
|
||||
@"HORIZONTAL TABULATION",
|
||||
@"LINE FEED",
|
||||
@"VERTICAL TABULATION",
|
||||
@"FORM FEED",
|
||||
@"CARRIAGE RETURN",
|
||||
@"SHIFT OUT",
|
||||
@"SHIFT IN",
|
||||
@"DATA LINK ESCAPE",
|
||||
@"DEVICE CONTROL ONE",
|
||||
@"DEVICE CONTROL TWO",
|
||||
@"DEVICE CONTROL THREE",
|
||||
@"DEVICE CONTROL FOUR",
|
||||
@"NEGATIVE ACKNOWLEDGE",
|
||||
@"SYNCHRONOUS IDLE",
|
||||
@"END OF TRANSMISSION BLOCK",
|
||||
@"CANCEL",
|
||||
@"END OF MEDIUM",
|
||||
@"SUBSTITUTE",
|
||||
@"ESCAPE",
|
||||
@"FILE SEPARATOR",
|
||||
@"GROUP SEPARATOR",
|
||||
@"RECORD SEPARATOR",
|
||||
@"UNIT SEPARATOR",
|
||||
@"SPACE",
|
||||
};
|
||||
|
||||
|
||||
static NSString *_Nonnull const CEDeleteCharacterName = @"DELETE";
|
||||
|
||||
|
||||
static NSString *_Nonnull const CEC1ControlCharacterNames[] = {
|
||||
@"PADDING CHARACTER",
|
||||
@"HIGH OCTET PRESET",
|
||||
@"BREAK PERMITTED HERE",
|
||||
@"NO BREAK HERE",
|
||||
@"INDEX",
|
||||
@"NEXT LINE",
|
||||
@"START OF SELECTED AREA",
|
||||
@"END OF SELECTED AREA",
|
||||
@"CHARACTER TABULATION SET",
|
||||
@"CHARACTER TABULATION WITH JUSTIFICATION",
|
||||
@"LINE TABULATION SET",
|
||||
@"PARTIAL LINE FORWARD",
|
||||
@"PARTIAL LINE BACKWARD",
|
||||
@"REVERSE LINE FEED",
|
||||
@"SINGLE SHIFT TWO",
|
||||
@"SINGLE SHIFT THREE",
|
||||
@"DEVICE CONTROL STRING",
|
||||
@"PRIVATE USE ONE",
|
||||
@"PRIVATE USE TWO",
|
||||
@"SET TRANSMIT STATE",
|
||||
@"CANCEL CHARACTER",
|
||||
@"MESSAGE WAITING",
|
||||
@"START OF PROTECTED AREA",
|
||||
@"END OF PROTECTED AREA",
|
||||
@"START OF STRING",
|
||||
@"SINGLE GRAPHIC CHARACTER INTRODUCER",
|
||||
@"SINGLE CHARACTER INTRODUCER",
|
||||
@"CONTROL SEQUENCE INTRODUCER",
|
||||
@"STRING TERMINATOR",
|
||||
@"OPERATING SYSTEM COMMAND",
|
||||
@"PRIVACY MESSAGE",
|
||||
@"APPLICATION PROGRAM COMMAND",
|
||||
};
|
||||
|
||||
|
||||
NSString * _Nullable CEControlCharacterName(unichar character)
|
||||
{
|
||||
if (CEIsC0ControlPoint(character)) {
|
||||
NSUInteger index = character;
|
||||
return CEC0ControlCharacterNames[index];
|
||||
|
||||
} else if (character == CEDeleteCharacter) {
|
||||
return CEDeleteCharacterName;
|
||||
|
||||
} else if (CEIsC1ControlPoint(character)) {
|
||||
NSUInteger index = character - 0x0080; // shift to 0-based array index
|
||||
return CEC1ControlCharacterNames[index];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
|
||||
CEUnicodeCharacter.h
|
||||
|
||||
CotEditor
|
||||
http://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-11-21.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 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
|
||||
|
||||
http://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;
|
||||
|
||||
|
||||
@interface CEUnicodeCharacter : NSObject
|
||||
|
||||
@property (nonatomic, readonly) UTF32Char character;
|
||||
@property (nonatomic, readonly) unichar pictureCharacter;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *unicode;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *string;
|
||||
@property (nonatomic, readonly, getter=isSurrogatePair) BOOL surrogatePair;
|
||||
@property (nonatomic, readonly, nullable, copy) NSArray<NSString *> *surrogateUnicodes;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *name;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *categoryName;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *blockName;
|
||||
@property (nonatomic, readonly, nonnull, copy) NSString *localizedBlockName;
|
||||
|
||||
|
||||
+ (nonnull CEUnicodeCharacter *)unicodeCharacterWithCharacter:(UTF32Char)character;
|
||||
|
||||
/// designated initializer
|
||||
- (nonnull instancetype)initWithCharacter:(UTF32Char)character NS_DESIGNATED_INITIALIZER;
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
@ -1,243 +0,0 @@
|
||||
/*
|
||||
|
||||
CEUnicodeCharacter.m
|
||||
|
||||
CotEditor
|
||||
http://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-11-21.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015-2016 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
|
||||
|
||||
http://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 ICU;
|
||||
|
||||
#import "CEUnicodeCharacter.h"
|
||||
#import "CEControlCharacerNames.h"
|
||||
|
||||
|
||||
@interface CEUnicodeCharacter ()
|
||||
|
||||
// readonly
|
||||
@property (nonatomic, readwrite) UTF32Char character;
|
||||
@property (nonatomic, readwrite) unichar pictureCharacter;
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *string;
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *unicode;
|
||||
@property (nonatomic, readwrite, getter=isSurrogatePair) BOOL surrogatePair;
|
||||
@property (nonatomic, readwrite, nullable, copy) NSArray<NSString *> *surrogateUnicodes;
|
||||
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *name;
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *categoryName;
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *blockName;
|
||||
@property (nonatomic, readwrite, nonnull, copy) NSString *localizedBlockName;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation CEUnicodeCharacter
|
||||
|
||||
//------------------------------------------------------
|
||||
/// disable superclass's designated initializer
|
||||
- (nonnull instancetype)init
|
||||
//------------------------------------------------------
|
||||
{
|
||||
@throw nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark Public Methods
|
||||
|
||||
//------------------------------------------------------
|
||||
/// convenience constractor
|
||||
+ (nonnull CEUnicodeCharacter *)unicodeCharacterWithCharacter:(UTF32Char)character
|
||||
//------------------------------------------------------
|
||||
{
|
||||
return [[CEUnicodeCharacter alloc] initWithCharacter:character];
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
/// designated initializer
|
||||
- (nonnull instancetype)initWithCharacter:(UTF32Char)character
|
||||
//------------------------------------------------------
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_character = character;
|
||||
_unicode = [NSString stringWithFormat:@"U+%04tX", character];
|
||||
|
||||
// surrogate pair check
|
||||
UniChar surrogates[2];
|
||||
_surrogatePair = CFStringGetSurrogatePairForLongCharacter(character, surrogates);
|
||||
if (_surrogatePair) {
|
||||
_surrogateUnicodes = @[[NSString stringWithFormat:@"U+%04X", surrogates[0]],
|
||||
[NSString stringWithFormat:@"U+%04X", surrogates[1]]];
|
||||
}
|
||||
|
||||
// UTF32Char to NSString
|
||||
BOOL isSingleSurrogate = CFStringIsSurrogateHighCharacter(character) || CFStringIsSurrogateLowCharacter(character);
|
||||
if (isSingleSurrogate) {
|
||||
unichar singleChar = character; // downcast
|
||||
_string = [[NSString alloc] initWithCharacters:&singleChar length:1];
|
||||
} else {
|
||||
UTF32Char littleEdian = NSSwapHostIntToLittle(character);
|
||||
_string = [[NSString alloc] initWithBytes:&littleEdian length:4 encoding:NSUTF32LittleEndianStringEncoding];
|
||||
}
|
||||
NSAssert(_string != nil, @"Failed to covnert UTF32Char U+%04tX into NSString.", character);
|
||||
|
||||
// alternate picture caracter for invisible control character
|
||||
if (CEIsC0ControlPoint(_character)) {
|
||||
_pictureCharacter = _character + 0x2400; // shift 0x2400 to Unicode control pictures
|
||||
} else if (_character == CEDeleteCharacter) { // DELETE character
|
||||
_pictureCharacter = 0x2421; // SYMBOL FOR DELETE character
|
||||
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
# pragma mark Public Accessors
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// Unicode name
|
||||
- (nonnull NSString *)name
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
// defer init
|
||||
if (!_name) {
|
||||
_name = CEControlCharacterName([self character]);
|
||||
|
||||
if (!_name) {
|
||||
NSMutableString *unicodeName = [[self string] mutableCopy];
|
||||
|
||||
// You can't use kCFStringTransformToUnicodeName instead of `Any-Name` here,
|
||||
// because some characters (e.g. normal `a`) don't return their name when use this constant.
|
||||
CFStringTransform((__bridge CFMutableStringRef)unicodeName, NULL, CFSTR("Any-Name"), NO);
|
||||
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\{(.+?)\\}" options:0 error:nil];
|
||||
NSTextCheckingResult *firstMatch = [regex firstMatchInString:unicodeName options:0
|
||||
range:NSMakeRange(0, [unicodeName length])];
|
||||
|
||||
_name = [unicodeName substringWithRange:[firstMatch rangeAtIndex:1]];
|
||||
}
|
||||
}
|
||||
|
||||
return _name;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// Unicode category name
|
||||
- (nonnull NSString *)categoryName
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
// defer init
|
||||
if (!_categoryName) {
|
||||
int32_t prop = u_getIntPropertyValue([self character], UCHAR_GENERAL_CATEGORY);
|
||||
const char *categoryNameChars = u_getPropertyValueName(UCHAR_GENERAL_CATEGORY, prop, U_LONG_PROPERTY_NAME);
|
||||
|
||||
_categoryName = [@(categoryNameChars) stringByReplacingOccurrencesOfString:@"_" withString:@" "]; // sanitize
|
||||
}
|
||||
|
||||
return _categoryName;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// Unicode block name just returned from an icu function
|
||||
- (nonnull NSString *)blockName
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
// defer init
|
||||
if (!_blockName) {
|
||||
int32_t prop = u_getIntPropertyValue([self character], UCHAR_BLOCK);
|
||||
const char *blockNameChars = u_getPropertyValueName(UCHAR_BLOCK, prop, U_LONG_PROPERTY_NAME);
|
||||
|
||||
_blockName = [@(blockNameChars) stringByReplacingOccurrencesOfString:@"_" withString:@" "]; // sanitize
|
||||
}
|
||||
|
||||
return _blockName;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// Localized and sanitized unicode block name
|
||||
- (nonnull NSString *)localizedBlockName
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
// defer init
|
||||
if (!_localizedBlockName) {
|
||||
NSString *blockName = [self blockName];
|
||||
|
||||
blockName = [[self class] sanitizeBlockName:blockName];
|
||||
|
||||
_localizedBlockName = NSLocalizedStringFromTable(blockName, @"Unicode", nil);
|
||||
|
||||
}
|
||||
return _localizedBlockName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark Private Methods
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// sanitize block name for localization
|
||||
+ (nonnull NSString *)sanitizeBlockName:(nonnull NSString *)blockName
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
// -> This is actually a dirty workaround to make the block name the same as the Apple's block naming rule.
|
||||
// Otherwise, we cannot localize block name correctly. (2015-11 by 1024jp)
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@" ([A-Z])$" withString:@"-$1"
|
||||
options:NSRegularExpressionSearch range:NSMakeRange(0, [blockName length])];
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@"Extension-" withString:@"Ext. "];
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@" And " withString:@" and "];
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@" For " withString:@" for "];
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@" Mathematical " withString:@" Math "];
|
||||
blockName = [blockName stringByReplacingOccurrencesOfString:@"Latin 1" withString:@"Latin-1"]; // only for "Latin-1
|
||||
|
||||
return blockName;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
/// check which block names will be lozalized (only for test use)
|
||||
+ (void)testUnicodeBlockNameLocalizationForLanguage:(NSString *)language
|
||||
// ------------------------------------------------------
|
||||
{
|
||||
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:language withExtension:@"lproj"];
|
||||
NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
|
||||
|
||||
for (int i = 0; i < UBLOCK_COUNT; i++) {
|
||||
const char *blockNameChars = u_getPropertyValueName(UCHAR_BLOCK, i, U_LONG_PROPERTY_NAME);
|
||||
|
||||
NSString *blockName = [@(blockNameChars) stringByReplacingOccurrencesOfString:@"_" withString:@" "]; // sanitize
|
||||
blockName = [[self class] sanitizeBlockName:blockName];
|
||||
|
||||
NSString *localizedBlockName = [bundle localizedStringForKey:blockName value:nil table:@"Unicode"];
|
||||
|
||||
NSLog(@"%@ %@ %@", [localizedBlockName isEqualToString:blockName] ? @"⚠️" : @" ", blockName, localizedBlockName);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -43,6 +43,7 @@ extension UnicodeScalar {
|
||||
static let textSequence = UnicodeScalar(0xFE0E)
|
||||
static let emojiSequence = UnicodeScalar(0xFE0F)
|
||||
|
||||
|
||||
var isVariantSelector: Bool {
|
||||
|
||||
let codePoint = self.value
|
||||
@ -56,13 +57,13 @@ extension UnicodeScalar {
|
||||
|
||||
// MARK:
|
||||
|
||||
class CharacterInfo: CustomDebugStringConvertible { // TODO: struct?
|
||||
class CharacterInfo: CustomStringConvertible, CustomDebugStringConvertible { // TODO: struct?
|
||||
|
||||
// MARK: Public Properties
|
||||
|
||||
let string: String
|
||||
let pictureString: String?
|
||||
let unicodes: [CEUnicodeCharacter]
|
||||
let unicodes: [UnicodeCharacter]
|
||||
let isComplex: Bool
|
||||
|
||||
|
||||
@ -80,58 +81,59 @@ class CharacterInfo: CustomDebugStringConvertible { // TODO: struct?
|
||||
guard string.numberOfComposedCharacters == 1 || string == "\r\n" else { return nil }
|
||||
// -> Number of String.characters.count and numberOfComposedCharacters are different.
|
||||
|
||||
let unicodes = string.unicodes
|
||||
|
||||
self.string = string
|
||||
self.unicodes = string.unicodes
|
||||
self.unicodes = unicodes
|
||||
|
||||
// check variation selector
|
||||
var additional: String?
|
||||
var isComplex = false
|
||||
if self.unicodes.count == 2 {
|
||||
let scalar = UnicodeScalar(unicodes.last!.character)
|
||||
let additional: String? = {
|
||||
guard unicodes.count == 2, let lastUnicode = unicodes.last?.character else { return nil }
|
||||
|
||||
switch (scalar) {
|
||||
switch lastUnicode {
|
||||
case UnicodeScalar.emojiSequence:
|
||||
additional = "Emoji Style"
|
||||
return "Emoji Style"
|
||||
|
||||
case UnicodeScalar.textSequence:
|
||||
additional = "Text Style"
|
||||
return "Text Style"
|
||||
|
||||
case SkinToneEmojiModifier.type12:
|
||||
additional = "Skin Tone I-II"
|
||||
return "Skin Tone I-II"
|
||||
|
||||
case SkinToneEmojiModifier.type3:
|
||||
additional = "Skin Tone III"
|
||||
return "Skin Tone III"
|
||||
|
||||
case SkinToneEmojiModifier.type4:
|
||||
additional = "Skin Tone IV"
|
||||
return "Skin Tone IV"
|
||||
|
||||
case SkinToneEmojiModifier.type5:
|
||||
additional = "Skin Tone V"
|
||||
return "Skin Tone V"
|
||||
|
||||
case SkinToneEmojiModifier.type6:
|
||||
additional = "Skin Tone VI"
|
||||
return "Skin Tone VI"
|
||||
|
||||
case let unicode where unicode.isVariantSelector:
|
||||
return "Variant"
|
||||
|
||||
default:
|
||||
if scalar.isVariantSelector {
|
||||
additional = "Variant"
|
||||
} else {
|
||||
isComplex = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
} else if self.unicodes.count > 2 {
|
||||
isComplex = true
|
||||
}
|
||||
}()
|
||||
self.variationSelectorAdditional = additional
|
||||
self.isComplex = isComplex
|
||||
self.isComplex = (unicodes.count > 1 && additional == nil)
|
||||
|
||||
var pictureString: String?
|
||||
if self.unicodes.count == 1 { // ignore CR/LF
|
||||
if let pictureCharacter = self.unicodes.first?.pictureCharacter, pictureCharacter != 0 {
|
||||
let scalar = UnicodeScalar(pictureCharacter)
|
||||
pictureString = String(scalar)
|
||||
}
|
||||
}
|
||||
self.pictureString = pictureString
|
||||
self.pictureString = {
|
||||
guard unicodes.count == 1, // ignore CR/LF
|
||||
let pictureCharacter = unicodes.first?.pictureCharacter else { return nil }
|
||||
|
||||
return String(Character(pictureCharacter))
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
var description: String {
|
||||
|
||||
return "\(self.string)"
|
||||
}
|
||||
|
||||
|
||||
@ -168,9 +170,9 @@ class CharacterInfo: CustomDebugStringConvertible { // TODO: struct?
|
||||
private extension String {
|
||||
|
||||
/// devide given string into UnicodeCharacter objects
|
||||
var unicodes: [CEUnicodeCharacter] {
|
||||
var unicodes: [UnicodeCharacter] {
|
||||
|
||||
return self.unicodeScalars.map { CEUnicodeCharacter(character: $0.value) }
|
||||
return self.unicodeScalars.map { UnicodeCharacter(character: $0) }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,8 +12,6 @@
|
||||
#import "CESyntaxManager.h"
|
||||
#import "CESyntaxDictionaryKeys.h"
|
||||
|
||||
#import "CEUnicodeCharacter.h"
|
||||
|
||||
// Document
|
||||
#import "CEPrintPanelAccessoryController.h"
|
||||
#import "NSTextView+CETextReplacement.h"
|
||||
|
@ -244,11 +244,10 @@ class DocumentAnalyzer: NSObject {
|
||||
|
||||
// unicode
|
||||
if needsAll && hasSelection {
|
||||
if let unicodes = CharacterInfo(string: selectedString)?.unicodes,
|
||||
let first = unicodes.first,
|
||||
unicodes.count == 1
|
||||
if selectedString.unicodeScalars.count == 1,
|
||||
let first = selectedString.unicodeScalars.first
|
||||
{
|
||||
unicode = first.unicode
|
||||
unicode = first.codePoint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
159
CotEditor/Sources/UnicodeCharacter.swift
Normal file
159
CotEditor/Sources/UnicodeCharacter.swift
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
|
||||
UnicodeCharacter.swift
|
||||
|
||||
CotEditor
|
||||
https://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-11-21.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015-2016 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
|
||||
|
||||
http://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 ICU
|
||||
|
||||
class UnicodeCharacter: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
|
||||
// MARK: Public Properties
|
||||
|
||||
let character: UnicodeScalar
|
||||
|
||||
/// Unicode name
|
||||
private(set) lazy var name: String = self.character.name ?? ""
|
||||
|
||||
/// alternate picture caracter for invisible control character
|
||||
private(set) lazy var pictureCharacter: UnicodeScalar? = self.character.pictureRepresentation
|
||||
|
||||
let unicode: String
|
||||
let string: String
|
||||
let isSurrogatePair: Bool
|
||||
let surrogateUnicodes: [String]?
|
||||
|
||||
|
||||
|
||||
// MARK:
|
||||
// MARK: Lifecycle
|
||||
|
||||
required init(character: UnicodeScalar) {
|
||||
|
||||
self.character = character
|
||||
self.unicode = character.codePoint
|
||||
|
||||
// surrogate pair check
|
||||
self.isSurrogatePair = character.isSurrogatePair
|
||||
if self.isSurrogatePair {
|
||||
self.surrogateUnicodes = [String(format: "U+%04X", UTF16.leadSurrogate(character)),
|
||||
String(format: "U+%04X", UTF16.trailSurrogate(character))]
|
||||
} else {
|
||||
self.surrogateUnicodes = nil
|
||||
}
|
||||
|
||||
// UnicodeScalar to String
|
||||
self.string = String(Character(character))
|
||||
}
|
||||
|
||||
|
||||
var description: String {
|
||||
|
||||
return "\(self.character)"
|
||||
}
|
||||
|
||||
|
||||
var debugDescription: String {
|
||||
|
||||
return "<\(self): \(self.character)>"
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Lazy Properties
|
||||
|
||||
/// Unicode category name
|
||||
private(set) lazy var categoryName: String = {
|
||||
|
||||
guard let name = self.character.categoryName else {
|
||||
return NSLocalizedString("Unknown category", tableName: "Unicode", comment: "")
|
||||
}
|
||||
|
||||
return name
|
||||
}()
|
||||
|
||||
|
||||
/// Unicode block name just returned from an icu function
|
||||
private(set) lazy var blockName: String = {
|
||||
|
||||
guard let name = self.character.blockName else {
|
||||
return NSLocalizedString("Unknown block", tableName: "Unicode", comment: "")
|
||||
}
|
||||
|
||||
return name
|
||||
}()
|
||||
|
||||
|
||||
/// Localized and sanitized unicode block name
|
||||
private(set) lazy var localizedBlockName: String = {
|
||||
|
||||
let blockName = sanitize(blockName: self.blockName)
|
||||
|
||||
return NSLocalizedString(sanitize(blockName: self.blockName), tableName: "Unicode", comment: "")
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// sanitize block name for localization
|
||||
private func sanitize(blockName: String) -> String
|
||||
{
|
||||
// -> This is actually a dirty workaround to make the block name the same as the Apple's block naming rule.
|
||||
// Otherwise, we cannot localize block name correctly. (2015-11 by 1024jp)
|
||||
|
||||
var sanitized = blockName
|
||||
|
||||
sanitized = sanitized.replacingOccurrences(of: " ([A-Z])$", with: "-$1", options: .regularExpression)
|
||||
sanitized = sanitized.replacingOccurrences(of: "Extension-", with: "Ext. ")
|
||||
sanitized = sanitized.replacingOccurrences(of: " And ", with: " and ")
|
||||
sanitized = sanitized.replacingOccurrences(of: " For ", with: " for ")
|
||||
sanitized = sanitized.replacingOccurrences(of: " Mathematical ", with: " Math ")
|
||||
sanitized = sanitized.replacingOccurrences(of: "Latin 1", with: "Latin-1") // only for "Latin-1
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Test
|
||||
|
||||
/// check which block names will be lozalized (only for test use)
|
||||
private func testUnicodeBlockNameLocalization(for language: String) {
|
||||
|
||||
let bundleURL = Bundle.main.urlForResource(language, withExtension: "lproj")!
|
||||
let bundle = Bundle(url: bundleURL)
|
||||
|
||||
for index in 0..<UBLOCK_COUNT.rawValue {
|
||||
let blockNameChars = u_getPropertyValueName(UCHAR_BLOCK, index, U_LONG_PROPERTY_NAME)
|
||||
|
||||
var blockName = String(blockNameChars).replacingOccurrences(of: "_", with: " ") // sanitize
|
||||
blockName = sanitize(blockName: blockName)
|
||||
|
||||
let localizedBlockName = bundle?.localizedString(forKey: blockName, value: nil, table: "Unicode")
|
||||
|
||||
print((localizedBlockName == blockName) ? "⚠️" : " ", blockName, localizedBlockName)
|
||||
}
|
||||
}
|
@ -42,20 +42,14 @@ class UnicodeInputPanelController: NSWindowController, NSTextFieldDelegate {
|
||||
|
||||
static let shared = UnicodeInputPanelController()
|
||||
|
||||
dynamic var characterString: String? {
|
||||
|
||||
return self.character?.string
|
||||
}
|
||||
private(set) dynamic var characterString: String?
|
||||
|
||||
|
||||
// MARK: Private Properties
|
||||
|
||||
private dynamic var unicode = ""
|
||||
private dynamic var codePoint: String?
|
||||
private dynamic var isValid = false
|
||||
|
||||
private dynamic var character: CEUnicodeCharacter?
|
||||
|
||||
private let unicodeRegex = try! RegularExpression(pattern: "^(?:U\\+|0x|\\\\u)?([0-9a-f]{1,5})$", options: .caseInsensitive)
|
||||
private dynamic var unicodeName: String?
|
||||
|
||||
|
||||
|
||||
@ -104,12 +98,26 @@ class UnicodeInputPanelController: NSWindowController, NSTextFieldDelegate {
|
||||
/// text in text field was changed
|
||||
override func controlTextDidChange(_ obj: Notification) {
|
||||
|
||||
guard let input = (obj.object as? NSTextField)?.stringValue else { return }
|
||||
var isValid = false
|
||||
var unicodeName: String? = nil
|
||||
var characterString: String? = nil
|
||||
|
||||
let result = self.unicodeRegex.firstMatch(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
|
||||
defer {
|
||||
self.isValid = isValid
|
||||
self.unicodeName = unicodeName
|
||||
self.characterString = characterString
|
||||
}
|
||||
|
||||
self.isValid = (result != nil)
|
||||
self.character = self.isValid ? CEUnicodeCharacter(character: self.longChar) : nil
|
||||
guard
|
||||
let input = (obj.object as? NSTextField)?.stringValue,
|
||||
let longChar = UInt32(codePoint: input) else { return }
|
||||
|
||||
unicodeName = longChar.unicodeName
|
||||
|
||||
guard let scalar = UnicodeScalar(codePoint: longChar) else { return }
|
||||
|
||||
isValid = true
|
||||
characterString = String(Character(scalar))
|
||||
}
|
||||
|
||||
|
||||
@ -128,25 +136,28 @@ class UnicodeInputPanelController: NSWindowController, NSTextFieldDelegate {
|
||||
|
||||
receiver.insertUnicodeCharacter(self)
|
||||
|
||||
self.unicode = ""
|
||||
self.character = nil
|
||||
self.codePoint = ""
|
||||
self.isValid = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Private Methods
|
||||
|
||||
/// UTF32Char form of current input unicode codepoint
|
||||
private var longChar: UTF32Char {
|
||||
|
||||
let scanner = Scanner(string: self.unicode)
|
||||
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "uU+\\")
|
||||
|
||||
var longChar: UTF32Char = 0
|
||||
scanner.scanHexInt32(&longChar)
|
||||
|
||||
return longChar
|
||||
self.unicodeName = nil
|
||||
self.characterString = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Private Methods
|
||||
|
||||
private extension UInt32 {
|
||||
|
||||
/// initialize from a possible Unicode code point representation like `U+1F600`, `1f600`, `0x1F600` and so on.
|
||||
init?(codePoint: String) {
|
||||
|
||||
guard let range = codePoint.range(of: "(?<=^(U\\+|0x|\\\\u)?)[0-9a-f]{1,5}$",
|
||||
options: [.regularExpression, .caseInsensitive]) else { return nil }
|
||||
let hexString = codePoint.substring(with: range)
|
||||
|
||||
self.init(hexString, radix: 16)
|
||||
}
|
||||
|
||||
}
|
||||
|
160
CotEditor/Sources/UnicodeScalar+ControlCharacter.swift
Normal file
160
CotEditor/Sources/UnicodeScalar+ControlCharacter.swift
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
|
||||
UnicodeScalar+ControlCharacter.swift
|
||||
|
||||
CotEditor
|
||||
http://coteditor.com
|
||||
|
||||
Created by 1024jp on 2015-12-29.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015-2016 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
|
||||
|
||||
http://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
|
||||
|
||||
extension UnicodeScalar {
|
||||
|
||||
/// alternate picture caracter for invisible control character
|
||||
var pictureRepresentation: UnicodeScalar? {
|
||||
|
||||
if ControlCharacter.C0Range.contains(self.value) {
|
||||
return UnicodeScalar(self.value + 0x2400) // shift 0x2400 to Unicode control pictures
|
||||
}
|
||||
if self.value == ControlCharacter.deleteCharacter { // DELETE character
|
||||
return UnicodeScalar(0x2421) // SYMBOL FOR DELETE character
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension UTF32Char {
|
||||
|
||||
/// unicode name if receiver is a control character
|
||||
var controlCharacterName: String? {
|
||||
|
||||
if ControlCharacter.C0Range.contains(self) {
|
||||
let index = Int(self)
|
||||
return ControlCharacter.C0Names[index]
|
||||
}
|
||||
if self == ControlCharacter.deleteCharacter {
|
||||
return ControlCharacter.deleteName
|
||||
}
|
||||
if ControlCharacter.C1Range.contains(self) {
|
||||
let index = Int(self - ControlCharacter.C1Range.lowerBound) // shift to 0-based array index
|
||||
return ControlCharacter.C1Names[index]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
enum ControlCharacter {
|
||||
|
||||
// MARK: Code Point Ranges
|
||||
|
||||
static let deleteCharacter = UInt32(0x007F)
|
||||
static let C0Range = UInt32(0x0000)...UInt32(0x0020) // U+0020 is actually not in range of C0 control character. But they are often included in actual fact.
|
||||
static let C1Range = UInt32(0x0080)...UInt32(0x009F)
|
||||
|
||||
|
||||
// MARK: Names
|
||||
|
||||
static let deleteName = "DELETE"
|
||||
|
||||
static let C0Names = [
|
||||
"NULL",
|
||||
"START OF HEADING",
|
||||
"START OF TEXT",
|
||||
"END OF TEXT",
|
||||
"END OF TRANSMISSION",
|
||||
"ENQUIRY",
|
||||
"ACKNOWLEDGE",
|
||||
"BELL",
|
||||
"BACKSPACE",
|
||||
"HORIZONTAL TABULATION",
|
||||
"LINE FEED",
|
||||
"VERTICAL TABULATION",
|
||||
"FORM FEED",
|
||||
"CARRIAGE RETURN",
|
||||
"SHIFT OUT",
|
||||
"SHIFT IN",
|
||||
"DATA LINK ESCAPE",
|
||||
"DEVICE CONTROL ONE",
|
||||
"DEVICE CONTROL TWO",
|
||||
"DEVICE CONTROL THREE",
|
||||
"DEVICE CONTROL FOUR",
|
||||
"NEGATIVE ACKNOWLEDGE",
|
||||
"SYNCHRONOUS IDLE",
|
||||
"END OF TRANSMISSION BLOCK",
|
||||
"CANCEL",
|
||||
"END OF MEDIUM",
|
||||
"SUBSTITUTE",
|
||||
"ESCAPE",
|
||||
"FILE SEPARATOR",
|
||||
"GROUP SEPARATOR",
|
||||
"RECORD SEPARATOR",
|
||||
"UNIT SEPARATOR",
|
||||
"SPACE",
|
||||
]
|
||||
|
||||
static let C1Names = [
|
||||
"PADDING CHARACTER",
|
||||
"HIGH OCTET PRESET",
|
||||
"BREAK PERMITTED HERE",
|
||||
"NO BREAK HERE",
|
||||
"INDEX",
|
||||
"NEXT LINE",
|
||||
"START OF SELECTED AREA",
|
||||
"END OF SELECTED AREA",
|
||||
"CHARACTER TABULATION SET",
|
||||
"CHARACTER TABULATION WITH JUSTIFICATION",
|
||||
"LINE TABULATION SET",
|
||||
"PARTIAL LINE FORWARD",
|
||||
"PARTIAL LINE BACKWARD",
|
||||
"REVERSE LINE FEED",
|
||||
"SINGLE SHIFT TWO",
|
||||
"SINGLE SHIFT THREE",
|
||||
"DEVICE CONTROL STRING",
|
||||
"PRIVATE USE ONE",
|
||||
"PRIVATE USE TWO",
|
||||
"SET TRANSMIT STATE",
|
||||
"CANCEL CHARACTER",
|
||||
"MESSAGE WAITING",
|
||||
"START OF PROTECTED AREA",
|
||||
"END OF PROTECTED AREA",
|
||||
"START OF STRING",
|
||||
"SINGLE GRAPHIC CHARACTER INTRODUCER",
|
||||
"SINGLE CHARACTER INTRODUCER",
|
||||
"CONTROL SEQUENCE INTRODUCER",
|
||||
"STRING TERMINATOR",
|
||||
"OPERATING SYSTEM COMMAND",
|
||||
"PRIVACY MESSAGE",
|
||||
"APPLICATION PROGRAM COMMAND",
|
||||
]
|
||||
|
||||
}
|
143
CotEditor/Sources/UnicodeScalar+Information.swift
Normal file
143
CotEditor/Sources/UnicodeScalar+Information.swift
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
|
||||
UnicodeScalar+Information.swift
|
||||
|
||||
CotEditor
|
||||
https://coteditor.com
|
||||
|
||||
Created by 1024jp on 2016-07-26.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015-2016 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
|
||||
|
||||
http://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 ICU
|
||||
|
||||
|
||||
extension UnicodeScalar {
|
||||
|
||||
/// initialize only if code point is in valid range for a UnicodeScalar
|
||||
init?(codePoint: UInt32) {
|
||||
|
||||
let surrogateRange = UInt32(0xD800)...UInt32(0xDFFF) // high- and low-surrogate code points are not valid Unicode scalar values
|
||||
let codeSpaceEdge: UInt32 = 0x10FFFF // value is outside of Unicode codespace
|
||||
|
||||
guard !surrogateRange.contains(codePoint) && codePoint <= codeSpaceEdge else { return nil }
|
||||
|
||||
self.init(codePoint)
|
||||
}
|
||||
|
||||
|
||||
/// code point string in format like `U+000F`
|
||||
var codePoint: String {
|
||||
|
||||
return String(format: "U+%04tX", self.value)
|
||||
}
|
||||
|
||||
|
||||
var isSurrogatePair: Bool {
|
||||
|
||||
return (UTF16.width(self) == 2)
|
||||
}
|
||||
|
||||
|
||||
/// Unicode name
|
||||
var name: String? {
|
||||
|
||||
return UTF32Char(self.value).unicodeName
|
||||
}
|
||||
|
||||
|
||||
/// Unicode category name just returned from the ICU function
|
||||
var categoryName: String? {
|
||||
|
||||
return UTF32Char(self.value).categoryName
|
||||
}
|
||||
|
||||
|
||||
/// Unicode block name just returned from the ICU function
|
||||
var blockName: String? {
|
||||
|
||||
return UTF32Char(self.value).blockName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
// Implemented Unicode functions at UTF32Char level in order to cover single surrogate character that is not allowed by UnicodeScalar
|
||||
|
||||
extension UTF32Char {
|
||||
|
||||
/// get Unicode name
|
||||
var unicodeName: String? {
|
||||
|
||||
if let name = self.controlCharacterName {
|
||||
return name
|
||||
}
|
||||
|
||||
return self.name(for: U_UNICODE_CHAR_NAME) ?? self.name(for: U_EXTENDED_CHAR_NAME)
|
||||
// -> `U_UNICODE_CHAR_NAME` returns modern Unicode name however it doesn't support surrogate character names.
|
||||
// `U_EXTENDED_CHAR_NAME` returns lowercase name within angle brackets like "<lead surrogate-D83D>".
|
||||
// Therefore, we combinate `U_UNICODE_CHAR_NAME` and `U_EXTENDED_CHAR_NAME`.
|
||||
}
|
||||
|
||||
|
||||
/// Unicode category name just returned from the ICU function
|
||||
var categoryName: String? {
|
||||
|
||||
return self.property(for: UCHAR_GENERAL_CATEGORY)
|
||||
}
|
||||
|
||||
|
||||
/// Unicode block name just returned from the ICU function
|
||||
var blockName: String? {
|
||||
|
||||
return self.property(for: UCHAR_BLOCK)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: Private Methods
|
||||
|
||||
/// get character name with name type
|
||||
private func name(for type: UCharNameChoice) -> String? {
|
||||
|
||||
var buffer = [CChar](repeating: 0, count: 128)
|
||||
var error = U_ZERO_ERROR
|
||||
u_charName(UChar32(self), type, &buffer, 128, &error)
|
||||
|
||||
guard error == U_ZERO_ERROR,
|
||||
let name = String(utf8String: buffer), !name.isEmpty else { return nil }
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
|
||||
/// get Unicode property for property key
|
||||
private func property(for property: UProperty) -> String? {
|
||||
|
||||
let prop = u_getIntPropertyValue(UChar32(self), property)
|
||||
guard let name = u_getPropertyValueName(property, prop, U_LONG_PROPERTY_NAME) else { return nil }
|
||||
|
||||
return String(cString: name).replacingOccurrences(of: "_", with: " ")
|
||||
}
|
||||
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 1024jp
|
||||
© 2015-2016 1024jp
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -254,3 +254,7 @@
|
||||
"Gujarati" = "Gujarati";
|
||||
"Tai Xuan Jing Symbols" = "Tai Xuan Jing Symbole";
|
||||
"Kana Supplement" = "Ergänzungen zu Kana";
|
||||
"Unknown block" = "Unknown block"; // FIXME: added
|
||||
|
||||
// Unicode category names
|
||||
"Unknown category" = "Unknown category"; // FIXME: added
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 1024jp
|
||||
© 2015-2016 1024jp
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -254,3 +254,7 @@
|
||||
"Gujarati" = "グジャラート文字";
|
||||
"Tai Xuan Jing Symbols" = "太玄経の記号";
|
||||
"Kana Supplement" = "かな補助";
|
||||
"Unknown block" = "不明なブロック";
|
||||
|
||||
// Unicode category names
|
||||
"Unknown category" = "不明なカテゴリ";
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 1024jp
|
||||
© 2015-2016 1024jp
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -254,3 +254,8 @@
|
||||
"Gujarati" = "古吉拉特文";
|
||||
"Tai Xuan Jing Symbols" = "太玄经符号";
|
||||
"Kana Supplement" = "假名补充";
|
||||
"Unknown block" = "Unknown block"; // FIXME: added
|
||||
|
||||
|
||||
// Unicode category names
|
||||
"Unknown category" = "Unknown category"; // FIXME: added
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
© 2015 1024jp
|
||||
© 2015-2016 1024jp
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -31,10 +31,26 @@ import XCTest
|
||||
|
||||
class CharacterInfoTests: XCTestCase {
|
||||
|
||||
// MARK: - CEUnicodeCharacter Tests
|
||||
// MARK: UTF32Char Extension Tests
|
||||
|
||||
func testSingleSurrogate() {
|
||||
|
||||
let character = UTF32Char(0xD83D)
|
||||
|
||||
XCTAssertEqual(character.unicodeName, "<lead surrogate-D83D>")
|
||||
XCTAssertEqual(character.categoryName, "Surrogate")
|
||||
XCTAssertEqual(character.blockName, "High Surrogates")
|
||||
|
||||
XCTAssertNil(UnicodeScalar(codePoint: character))
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - UnicodeCharacter Tests
|
||||
|
||||
func testSingleChar() {
|
||||
let character = CEUnicodeCharacter(character: UTF32Char("あ"))
|
||||
|
||||
let character = UnicodeCharacter(character: UnicodeScalar("あ"))
|
||||
|
||||
XCTAssertEqual(CChar32(character.character), CChar32("あ"))
|
||||
XCTAssertEqual(character.unicode, "U+3042")
|
||||
@ -48,18 +64,9 @@ class CharacterInfoTests: XCTestCase {
|
||||
}
|
||||
|
||||
|
||||
func testSingleSurrogate() {
|
||||
let character = CEUnicodeCharacter(character: UTF32Char(0xD83D))
|
||||
|
||||
XCTAssertEqual(character.unicode, "U+D83D")
|
||||
XCTAssertEqual(character.name, "<lead surrogate-D83D>")
|
||||
XCTAssertEqual(character.categoryName, "Surrogate")
|
||||
XCTAssertEqual(character.blockName, "High Surrogates")
|
||||
}
|
||||
|
||||
|
||||
func testSurrogateEmoji() {
|
||||
let character = CEUnicodeCharacter(character: UTF32Char("😀"))
|
||||
|
||||
let character = UnicodeCharacter(character: UnicodeScalar("😀"))
|
||||
|
||||
XCTAssertEqual(CChar32(character.character), CChar32("😀"))
|
||||
XCTAssertEqual(character.unicode, "U+1F600")
|
||||
@ -74,7 +81,8 @@ class CharacterInfoTests: XCTestCase {
|
||||
|
||||
|
||||
func testUnicodeBlockNameWithHyphen() {
|
||||
let character = CEUnicodeCharacter(character: UTF32Char("﷽"))
|
||||
|
||||
let character = UnicodeCharacter(character: UnicodeScalar("﷽"))
|
||||
|
||||
XCTAssertEqual(character.unicode, "U+FDFD")
|
||||
XCTAssertEqual(character.name, "ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM")
|
||||
@ -83,43 +91,46 @@ class CharacterInfoTests: XCTestCase {
|
||||
|
||||
|
||||
func testUnicodeControlPictures() {
|
||||
|
||||
// test NULL
|
||||
let nullCharacter = CEUnicodeCharacter(character: UTF32Char(0x0000))
|
||||
let nullPictureCharacter = CEUnicodeCharacter(character: UTF32Char(0x2400))
|
||||
let nullCharacter = UnicodeCharacter(character: UnicodeScalar(0x0000))
|
||||
let nullPictureCharacter = UnicodeCharacter(character: UnicodeScalar(0x2400))
|
||||
XCTAssertEqual(nullCharacter.name, "NULL")
|
||||
XCTAssertEqual(nullPictureCharacter.name, "SYMBOL FOR NULL")
|
||||
XCTAssertEqual(nullCharacter.pictureCharacter, unichar(nullPictureCharacter.character))
|
||||
XCTAssertEqual(nullCharacter.pictureCharacter, nullPictureCharacter.character)
|
||||
|
||||
// test SPACE
|
||||
let spaceCharacter = CEUnicodeCharacter(character: UTF32Char(0x0020))
|
||||
let spacePictureCharacter = CEUnicodeCharacter(character: UTF32Char(0x2420))
|
||||
let spaceCharacter = UnicodeCharacter(character: UnicodeScalar(0x0020))
|
||||
let spacePictureCharacter = UnicodeCharacter(character: UnicodeScalar(0x2420))
|
||||
XCTAssertEqual(spaceCharacter.name, "SPACE")
|
||||
XCTAssertEqual(spacePictureCharacter.name, "SYMBOL FOR SPACE")
|
||||
XCTAssertEqual(spaceCharacter.pictureCharacter, unichar(spacePictureCharacter.character))
|
||||
XCTAssertEqual(spaceCharacter.pictureCharacter, spacePictureCharacter.character)
|
||||
|
||||
// test DELETE
|
||||
XCTAssertEqual(Int(CEDeleteCharacter), NSDeleteCharacter)
|
||||
let deleteCharacter = CEUnicodeCharacter(character: UTF32Char(NSDeleteCharacter))
|
||||
let deletePictureCharacter = CEUnicodeCharacter(character: UTF32Char("␡"))
|
||||
XCTAssertEqual(Int(ControlCharacter.deleteCharacter), NSDeleteCharacter)
|
||||
let deleteCharacter = UnicodeCharacter(character: UnicodeScalar(NSDeleteCharacter))
|
||||
let deletePictureCharacter = UnicodeCharacter(character: UnicodeScalar("␡"))
|
||||
XCTAssertEqual(deleteCharacter.name, "DELETE")
|
||||
XCTAssertEqual(deletePictureCharacter.name, "SYMBOL FOR DELETE")
|
||||
XCTAssertEqual(deleteCharacter.pictureCharacter, unichar(deletePictureCharacter.character))
|
||||
XCTAssertEqual(deleteCharacter.pictureCharacter, deletePictureCharacter.character)
|
||||
|
||||
// test one after the last C0 control character
|
||||
let exclamationCharacter = CEUnicodeCharacter(character: UTF32Char(0x0021))
|
||||
let exclamationCharacter = UnicodeCharacter(character: UnicodeScalar(0x0021))
|
||||
XCTAssertEqual(exclamationCharacter.name, "EXCLAMATION MARK")
|
||||
XCTAssertEqual(exclamationCharacter.pictureCharacter, 0)
|
||||
XCTAssertNil(exclamationCharacter.pictureCharacter)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CharacterInfo Tests
|
||||
|
||||
func testMultiCharString() {
|
||||
|
||||
XCTAssertNil(CharacterInfo(string: "foo"))
|
||||
}
|
||||
|
||||
|
||||
func testSingleCharWithVSInfo() {
|
||||
|
||||
guard let charInfo = CharacterInfo(string: "☺︎") else {
|
||||
XCTFail()
|
||||
return
|
||||
@ -127,36 +138,39 @@ class CharacterInfoTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(charInfo.string, "☺︎")
|
||||
XCTAssertFalse(charInfo.isComplex)
|
||||
XCTAssertEqual(charInfo.unicodes.map{$0.unicode}, ["U+263A", "U+FE0E"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{$0.name}, ["WHITE SMILING FACE", "VARIATION SELECTOR-15"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{ $0.unicode }, ["U+263A", "U+FE0E"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{ $0.name }, ["WHITE SMILING FACE", "VARIATION SELECTOR-15"])
|
||||
XCTAssertEqual(charInfo.localizedDescription, "WHITE SMILING FACE (Text Style)")
|
||||
}
|
||||
|
||||
|
||||
func testCombiningCharacterInfo() {
|
||||
|
||||
guard let charInfo = CharacterInfo(string: "1️⃣") else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(charInfo.isComplex)
|
||||
XCTAssertEqual(charInfo.unicodes.map{$0.unicode}, ["U+0031", "U+FE0F", "U+20E3"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{ $0.unicode }, ["U+0031", "U+FE0F", "U+20E3"])
|
||||
XCTAssertEqual(charInfo.localizedDescription, "<a letter consisting of 3 characters>")
|
||||
}
|
||||
|
||||
|
||||
func testNationalIndicatorInfo() {
|
||||
|
||||
guard let charInfo = CharacterInfo(string: "🇯🇵") else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertTrue(charInfo.isComplex)
|
||||
XCTAssertEqual(charInfo.unicodes.map{$0.unicode}, ["U+1F1EF", "U+1F1F5"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{ $0.unicode }, ["U+1F1EF", "U+1F1F5"])
|
||||
}
|
||||
|
||||
|
||||
func testControlCharacterInfo() {
|
||||
|
||||
guard let charInfo = CharacterInfo(string: " ") else {
|
||||
XCTFail()
|
||||
return
|
||||
@ -164,7 +178,7 @@ class CharacterInfoTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(charInfo.string, " ")
|
||||
XCTAssertEqual(charInfo.pictureString, "␠")
|
||||
XCTAssertEqual(charInfo.unicodes.map{$0.name}, ["SPACE"])
|
||||
XCTAssertEqual(charInfo.unicodes.map{ $0.name }, ["SPACE"])
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,9 +7,5 @@
|
||||
#import <NSHash/NSString+NSHash.h>
|
||||
#import "CESyntaxDictionaryKeys.h"
|
||||
|
||||
// Character Info Tests
|
||||
#import "CEControlCharacerNames.h"
|
||||
#import "CEUnicodeCharacter.h"
|
||||
|
||||
// Theme Tests
|
||||
#import "NSColor+WFColorCode.h"
|
||||
|
Loading…
Reference in New Issue
Block a user