@swiftbrowser/swift 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +200 -9
- package/README.md +7 -9
- package/Sources/AVFoundation/AVFoundation.swift +131 -0
- package/Sources/AVFoundation/Asset.swift +487 -0
- package/Sources/AVFoundation/AudioSession.swift +144 -0
- package/Sources/AVFoundation/CMTime.swift +303 -0
- package/Sources/AVFoundation/Capture.swift +688 -0
- package/Sources/AVFoundation/Player.swift +749 -0
- package/Sources/AVFoundation/PlayerView.swift +117 -0
- package/Sources/AVKit/AVKit.swift +132 -0
- package/Sources/Accelerate/Accelerate.swift +122 -0
- package/Sources/AppIntents/AppIntents.swift +144 -0
- package/Sources/AppIntents/Parameters.swift +220 -0
- package/Sources/AppIntents/Shortcuts.swift +159 -0
- package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
- package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
- package/Sources/AuthenticationServices/Authorization.swift +251 -0
- package/Sources/Charts/Charts.swift +0 -4
- package/Sources/Combine/AnyCancellable.swift +65 -0
- package/Sources/Combine/AnyPublisher.swift +148 -0
- package/Sources/Combine/AnySubscriber.swift +230 -0
- package/Sources/Combine/Cancellable.swift +35 -0
- package/Sources/Combine/Codable.swift +29 -0
- package/Sources/Combine/CombineIdentifier.swift +45 -0
- package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
- package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
- package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
- package/Sources/Combine/CurrentValueSubject.swift +252 -0
- package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
- package/Sources/Combine/Future.swift +228 -0
- package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
- package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
- package/Sources/Combine/Helpers/ConduitList.swift +59 -0
- package/Sources/Combine/Helpers/DebugHook.swift +177 -0
- package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
- package/Sources/Combine/Helpers/Locking.swift +30 -0
- package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
- package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
- package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
- package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
- package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
- package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
- package/Sources/Combine/Helpers/Utils.swift +30 -0
- package/Sources/Combine/Helpers/Violations.swift +33 -0
- package/Sources/Combine/ImmediateScheduler.swift +178 -0
- package/Sources/Combine/LICENSE +21 -0
- package/Sources/Combine/NotificationPublisher.swift +48 -0
- package/Sources/Combine/ObjectWillChange.swift +46 -0
- package/Sources/Combine/PassthroughSubject.swift +222 -0
- package/Sources/Combine/Publisher+Subscribe.swift +53 -0
- package/Sources/Combine/Publishers/Deferred.swift +48 -0
- package/Sources/Combine/Publishers/Empty.swift +54 -0
- package/Sources/Combine/Publishers/Fail.swift +42 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
- package/Sources/Combine/Publishers/Just.swift +313 -0
- package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
- package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
- package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
- package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
- package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
- package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
- package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
- package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
- package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
- package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
- package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
- package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
- package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
- package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
- package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
- package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
- package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
- package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
- package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
- package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
- package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
- package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
- package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
- package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
- package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
- package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
- package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
- package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
- package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
- package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
- package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
- package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
- package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
- package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
- package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
- package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
- package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
- package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
- package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
- package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
- package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
- package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
- package/Sources/Combine/Publishers/Publishers.swift +13 -0
- package/Sources/Combine/Publishers/Record.swift +237 -0
- package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
- package/Sources/Combine/Result.swift +46 -0
- package/Sources/Combine/Scheduler.swift +89 -0
- package/Sources/Combine/Schedulers.swift +211 -0
- package/Sources/Combine/Subject+Void.swift +17 -0
- package/Sources/Combine/Subscriber+Void.swift +20 -0
- package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
- package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
- package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
- package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
- package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
- package/Sources/Combine/Subscription.swift +22 -0
- package/Sources/Combine/Subscriptions.swift +42 -0
- package/Sources/Combine/SwiftUIObservation.swift +10 -0
- package/Sources/Combine/_Introspection.swift +107 -0
- package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
- package/Sources/Compression/Compression.swift +365 -0
- package/Sources/Compression/Deflate.swift +407 -0
- package/Sources/Compression/Zlib.swift +179 -0
- package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
- package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
- package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
- package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
- package/Sources/CoreImage/CIColor.swift +41 -0
- package/Sources/CoreImage/CIContext.swift +31 -0
- package/Sources/CoreImage/CIDetector.swift +57 -0
- package/Sources/CoreImage/CIFilter.swift +243 -0
- package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
- package/Sources/CoreImage/CIImage.swift +162 -0
- package/Sources/CoreImage/QRCode.swift +393 -0
- package/Sources/CoreLocation/Coordinate.swift +30 -0
- package/Sources/CoreLocation/Location.swift +65 -0
- package/Sources/CoreML/Evaluation.swift +414 -0
- package/Sources/CoreML/FeatureValue.swift +266 -0
- package/Sources/CoreML/Model.swift +269 -0
- package/Sources/CoreML/MultiArray.swift +92 -0
- package/Sources/CoreML/Specification.swift +233 -0
- package/Sources/CoreText/CoreText.swift +64 -0
- package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
- package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
- package/Sources/CryptoKit/CryptoKit.swift +375 -0
- package/Sources/CryptoKit/Curve25519.swift +317 -0
- package/Sources/CryptoKit/P256.swift +321 -0
- package/Sources/FoundationBridge/AppStorage.swift +165 -0
- package/Sources/FoundationBridge/AssetResources.swift +90 -0
- package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
- package/Sources/FoundationBridge/AsyncImage.swift +116 -0
- package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
- package/Sources/FoundationBridge/CombineExports.swift +8 -0
- package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
- package/Sources/FoundationBridge/Dispatch.swift +458 -0
- package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
- package/Sources/FoundationBridge/FileImporter.swift +128 -0
- package/Sources/FoundationBridge/FileSystem.swift +68 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
- package/Sources/FoundationBridge/HostResolution.swift +31 -0
- package/Sources/FoundationBridge/ItemProvider.swift +655 -0
- package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
- package/Sources/FoundationBridge/Links.swift +127 -0
- package/Sources/FoundationBridge/ListFormatter.swift +32 -0
- package/Sources/FoundationBridge/Localization.swift +149 -0
- package/Sources/FoundationBridge/Locks.swift +50 -0
- package/Sources/FoundationBridge/Markdown.swift +527 -0
- package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
- package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
- package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
- package/Sources/FoundationBridge/Operation.swift +195 -0
- package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
- package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
- package/Sources/FoundationBridge/SceneStorage.swift +78 -0
- package/Sources/FoundationBridge/SecurityExports.swift +8 -0
- package/Sources/FoundationBridge/Selector.swift +226 -0
- package/Sources/FoundationBridge/StringComparators.swift +17 -0
- package/Sources/FoundationBridge/Table.swift +211 -0
- package/Sources/FoundationBridge/TextAttributes.swift +42 -0
- package/Sources/FoundationBridge/Timer.swift +6 -1
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
- package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
- package/Sources/FoundationBridge/WebSocket.swift +187 -0
- package/Sources/FoundationModels/FoundationModels.swift +1098 -0
- package/Sources/ImageIO/GIF.swift +72 -0
- package/Sources/ImageIO/ImageIO.swift +248 -0
- package/Sources/ImageIO/ImageTypes.swift +28 -0
- package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
- package/Sources/LocalAuthentication/LAContext.swift +86 -0
- package/Sources/MapKit/Geometry.swift +223 -0
- package/Sources/MapKit/Map.swift +638 -0
- package/Sources/MapKit/MapView.swift +229 -0
- package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
- package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
- package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
- package/Sources/OSLog/OSLog.swift +6 -0
- package/Sources/Photos/Photos.swift +572 -0
- package/Sources/PhotosUI/PhotosPicker.swift +248 -0
- package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
- package/Sources/QuickLook/QuickLook.swift +168 -0
- package/Sources/SafariServices/SafariServices.swift +106 -0
- package/Sources/Security/Security.swift +332 -0
- package/Sources/StoreKit/AppStore.swift +207 -0
- package/Sources/StoreKit/Products.swift +280 -0
- package/Sources/StoreKit/RequestReview.swift +28 -0
- package/Sources/StoreKit/StoreKit1.swift +486 -0
- package/Sources/StoreKit/StoreViews.swift +428 -0
- package/Sources/StoreKit/Transactions.swift +197 -0
- package/Sources/SwiftData/Macros.swift +58 -0
- package/Sources/SwiftData/ModelActor.swift +38 -0
- package/Sources/SwiftData/ModelContainer.swift +129 -0
- package/Sources/SwiftData/ModelContext.swift +451 -0
- package/Sources/SwiftData/PersistentModel.swift +238 -0
- package/Sources/SwiftData/Properties.swift +261 -0
- package/Sources/SwiftData/Query.swift +256 -0
- package/Sources/SwiftData/Schema.swift +192 -0
- package/Sources/SwiftData/Store.swift +146 -0
- package/Sources/SwiftData/StringPredicates.swift +125 -0
- package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
- package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
- package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
- package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
- package/Sources/SwiftDataMacros/Plugin.swift +18 -0
- package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
- package/Sources/SwiftUI/API/Accessibility.swift +55 -6
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +1 -1
- package/Sources/SwiftUI/API/AlignmentGuides.swift +68 -0
- package/Sources/SwiftUI/API/Animation.swift +37 -5
- package/Sources/SwiftUI/API/App.swift +31 -1
- package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
- package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
- package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
- package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
- package/Sources/SwiftUI/API/Canvas.swift +337 -0
- package/Sources/SwiftUI/API/Color.swift +104 -28
- package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
- package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
- package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
- package/Sources/SwiftUI/API/Commands.swift +106 -0
- package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
- package/Sources/SwiftUI/API/ContentShape.swift +33 -0
- package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
- package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
- package/Sources/SwiftUI/API/Controls.swift +59 -27
- package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +1 -1
- package/Sources/SwiftUI/API/EditMode.swift +25 -82
- package/Sources/SwiftUI/API/Effects.swift +125 -10
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +100 -9
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +11 -3
- package/Sources/SwiftUI/API/Gauge.swift +26 -10
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +1 -1
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +1 -1
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
- package/Sources/SwiftUI/API/Layout.swift +71 -12
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +61 -6
- package/Sources/SwiftUI/API/Menu.swift +8 -1
- package/Sources/SwiftUI/API/Modifiers.swift +88 -12
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +24 -9
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +14 -4
- package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
- package/Sources/SwiftUI/API/Popover.swift +41 -0
- package/Sources/SwiftUI/API/Preferences.swift +262 -0
- package/Sources/SwiftUI/API/Presentation.swift +17 -4
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +168 -39
- package/Sources/SwiftUI/API/Publishers.swift +4 -4
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +3 -2
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
- package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
- package/Sources/SwiftUI/API/SectionSupplements.swift +86 -0
- package/Sources/SwiftUI/API/Shape.swift +214 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +187 -8
- package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
- package/Sources/SwiftUI/API/Sharing.swift +193 -0
- package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
- package/Sources/SwiftUI/API/State.swift +18 -1
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
- package/Sources/SwiftUI/API/TabContent.swift +841 -0
- package/Sources/SwiftUI/API/Table.swift +382 -0
- package/Sources/SwiftUI/API/Tabs.swift +148 -4
- package/Sources/SwiftUI/API/Tasks.swift +2 -2
- package/Sources/SwiftUI/API/Text.swift +386 -0
- package/Sources/SwiftUI/API/TextDates.swift +61 -0
- package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
- package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
- package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
- package/Sources/SwiftUI/API/Toolbar.swift +52 -16
- package/Sources/SwiftUI/API/Transfer.swift +145 -0
- package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
- package/Sources/SwiftUI/API/Video.swift +64 -0
- package/Sources/SwiftUI/API/View.swift +46 -2
- package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
- package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +7 -1
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +17 -3
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Localization/Localization.swift +524 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
- package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
- package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
- package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
- package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
- package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
- package/Sources/Symbols/Symbols.swift +25 -0
- package/Sources/Translation/Translation.swift +423 -0
- package/Sources/UIKit/Activity.swift +213 -0
- package/Sources/UIKit/Alerts.swift +69 -0
- package/Sources/UIKit/AppDelegate.swift +222 -0
- package/Sources/UIKit/Appearance.swift +84 -0
- package/Sources/UIKit/ColorConversions.swift +70 -0
- package/Sources/UIKit/Controls.swift +225 -0
- package/Sources/UIKit/Device.swift +200 -0
- package/Sources/UIKit/Feedback.swift +65 -0
- package/Sources/UIKit/Font.swift +307 -0
- package/Sources/UIKit/Graphics.swift +204 -0
- package/Sources/UIKit/HostingController.swift +185 -0
- package/Sources/UIKit/Identity.swift +24 -0
- package/Sources/UIKit/Introspectable.swift +510 -0
- package/Sources/UIKit/Pasteboard.swift +363 -0
- package/Sources/UIKit/PhotoAlbum.swift +81 -0
- package/Sources/UIKit/RemoteNotifications.swift +15 -0
- package/Sources/UIKit/Representable.swift +288 -0
- package/Sources/UIKit/SafeArea.swift +12 -0
- package/Sources/UIKit/SceneDelegates.swift +132 -0
- package/Sources/UIKit/Scenes.swift +40 -0
- package/Sources/UIKit/ScrollView.swift +157 -0
- package/Sources/UIKit/ShortcutItems.swift +67 -0
- package/Sources/UIKit/SymbolConfiguration.swift +124 -0
- package/Sources/UIKit/TargetAction.swift +123 -0
- package/Sources/UIKit/TextPaste.swift +161 -0
- package/Sources/UIKit/TextView.swift +538 -0
- package/Sources/UIKit/UIKit.swift +292 -0
- package/Sources/UIKit/UIView.swift +855 -0
- package/Sources/UIKit/ViewTranslation.swift +311 -0
- package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
- package/Sources/UserNotifications/Content.swift +370 -0
- package/Sources/UserNotifications/NotificationCenter.swift +309 -0
- package/Sources/WeatherKit/WeatherKit.swift +228 -0
- package/Sources/WebKit/WebKit.swift +245 -0
- package/Sources/os/os.swift +376 -0
- package/Sources/zlib/zlib.swift +6 -0
- package/index.d.ts +1 -1
- package/index.mjs +2 -2
- package/manifest.d.mts +2 -0
- package/manifest.mjs +41 -0
- package/package.json +5 -3
- package/sync-sources.mjs +21 -7
- package/Sources/FoundationLite/Common.swift +0 -3
- package/Sources/FoundationLite/FoundationLite.swift +0 -171
- package/Sources/FoundationLite/Interaction.swift +0 -146
- package/Sources/FoundationLite/Timer.swift +0 -48
- package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
// `AttributedString(markdown:options:baseURL:)`, which swift-foundation has
|
|
2
|
+
// only on Apple platforms. It parses Markdown's inline syntax, as CommonMark
|
|
3
|
+
// and GitHub define it, into the attributes Apple's parser sets:
|
|
4
|
+
//
|
|
5
|
+
// - `inlinePresentationIntent`: emphasis (`*a*`, `_a_`), strong emphasis
|
|
6
|
+
// (`**a**`, `__a__`), code spans (`` `a` ``), strikethrough (`~a~`, `~~a~~`)
|
|
7
|
+
// and hard line breaks;
|
|
8
|
+
// - `link`, from `[text](url "title")` and `<scheme:…>` autolinks;
|
|
9
|
+
// - `imageURL`, from ``, on the alt text (EmojiText marks its
|
|
10
|
+
// custom emoji this way).
|
|
11
|
+
//
|
|
12
|
+
// Backslash escapes and HTML entities are decoded. Block syntax is not
|
|
13
|
+
// interpreted. With `.full`, blank lines separate paragraphs, and the
|
|
14
|
+
// paragraphs are concatenated, as Apple's parser does when it sets no block
|
|
15
|
+
// attributes. `.inlineOnly` turns line breaks into spaces;
|
|
16
|
+
// `.inlineOnlyPreservingWhitespace` keeps the text's whitespace as it is.
|
|
17
|
+
// Parsing never fails, so the initializer never throws.
|
|
18
|
+
|
|
19
|
+
import Foundation
|
|
20
|
+
|
|
21
|
+
/// How a run of text is presented inline: Markdown's emphasis, code and the like.
|
|
22
|
+
public struct InlinePresentationIntent: OptionSet, Hashable, Codable, Sendable {
|
|
23
|
+
public let rawValue: UInt
|
|
24
|
+
public init(rawValue: UInt) { self.rawValue = rawValue }
|
|
25
|
+
|
|
26
|
+
public static let emphasized = InlinePresentationIntent(rawValue: 1 << 0)
|
|
27
|
+
public static let stronglyEmphasized = InlinePresentationIntent(rawValue: 1 << 1)
|
|
28
|
+
public static let code = InlinePresentationIntent(rawValue: 1 << 2)
|
|
29
|
+
public static let strikethrough = InlinePresentationIntent(rawValue: 1 << 5)
|
|
30
|
+
public static let softBreak = InlinePresentationIntent(rawValue: 1 << 6)
|
|
31
|
+
public static let lineBreak = InlinePresentationIntent(rawValue: 1 << 7)
|
|
32
|
+
public static let inlineHTML = InlinePresentationIntent(rawValue: 1 << 8)
|
|
33
|
+
public static let blockHTML = InlinePresentationIntent(rawValue: 1 << 9)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
extension AttributeScopes.FoundationAttributes {
|
|
37
|
+
/// Markdown's inline presentation (`run.inlinePresentationIntent`).
|
|
38
|
+
public enum InlinePresentationIntentAttribute: CodableAttributedStringKey {
|
|
39
|
+
public typealias Value = InlinePresentationIntent
|
|
40
|
+
public static let name = "NSInlinePresentationIntent"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// For `attributedString.inlinePresentationIntent`: like the scope's own
|
|
44
|
+
/// attributes, the key path names the key and its value is never read.
|
|
45
|
+
public var inlinePresentationIntent: InlinePresentationIntentAttribute { fatalError("an attribute key path is never read") }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
extension AttributedString {
|
|
49
|
+
public struct MarkdownParsingOptions: Hashable, Sendable {
|
|
50
|
+
public enum InterpretedSyntax: Hashable, Sendable {
|
|
51
|
+
case full
|
|
52
|
+
case inlineOnly
|
|
53
|
+
case inlineOnlyPreservingWhitespace
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public enum FailurePolicy: Hashable, Sendable {
|
|
57
|
+
case throwError
|
|
58
|
+
case returnPartiallyParsedIfPossible
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public var allowsExtendedAttributes: Bool
|
|
62
|
+
public var interpretedSyntax: InterpretedSyntax
|
|
63
|
+
public var failurePolicy: FailurePolicy
|
|
64
|
+
public var languageCode: String?
|
|
65
|
+
public var appliesSourcePositionAttributes: Bool
|
|
66
|
+
|
|
67
|
+
public init(allowsExtendedAttributes: Bool = false, interpretedSyntax: InterpretedSyntax = .full,
|
|
68
|
+
failurePolicy: FailurePolicy = .throwError, languageCode: String? = nil,
|
|
69
|
+
appliesSourcePositionAttributes: Bool = false) {
|
|
70
|
+
self.allowsExtendedAttributes = allowsExtendedAttributes
|
|
71
|
+
self.interpretedSyntax = interpretedSyntax
|
|
72
|
+
self.failurePolicy = failurePolicy
|
|
73
|
+
self.languageCode = languageCode
|
|
74
|
+
self.appliesSourcePositionAttributes = appliesSourcePositionAttributes
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public init(markdown: String, options: MarkdownParsingOptions = .init(), baseURL: URL? = nil) throws {
|
|
79
|
+
self.init()
|
|
80
|
+
for segment in _MarkdownInline.segments(of: markdown, syntax: options.interpretedSyntax) {
|
|
81
|
+
var piece = AttributedString(segment.text)
|
|
82
|
+
if !segment.intent.isEmpty { piece[AttributeScopes.FoundationAttributes.InlinePresentationIntentAttribute.self] = segment.intent }
|
|
83
|
+
if let link = segment.link { piece.link = Self._url(link, relativeTo: baseURL) }
|
|
84
|
+
if let image = segment.image { piece.imageURL = Self._url(image, relativeTo: baseURL) }
|
|
85
|
+
self.append(piece)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public init(markdown: Data, options: MarkdownParsingOptions = .init(), baseURL: URL? = nil) throws {
|
|
90
|
+
try self.init(markdown: String(decoding: markdown, as: UTF8.self), options: options, baseURL: baseURL)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static func _url(_ string: String, relativeTo base: URL?) -> URL? {
|
|
94
|
+
if let url = URL(string: string, relativeTo: base) { return url }
|
|
95
|
+
let encoded = string.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed.union(.urlPathAllowed).union(CharacterSet(charactersIn: "?#:/")))
|
|
96
|
+
return encoded.flatMap { URL(string: $0, relativeTo: base) }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// Markdown's inline syntax, parsed into runs of text with their presentation.
|
|
101
|
+
enum _MarkdownInline {
|
|
102
|
+
struct Segment: Equatable {
|
|
103
|
+
var text: String
|
|
104
|
+
var intent: InlinePresentationIntent = []
|
|
105
|
+
var link: String?
|
|
106
|
+
var image: String?
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
indirect enum Node: Equatable {
|
|
110
|
+
case text(String)
|
|
111
|
+
case code(String)
|
|
112
|
+
case lineBreak
|
|
113
|
+
case styled(InlinePresentationIntent, [Node])
|
|
114
|
+
case link(String, [Node])
|
|
115
|
+
case image(String, [Node])
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static func segments(of markdown: String, syntax: AttributedString.MarkdownParsingOptions.InterpretedSyntax) -> [Segment] {
|
|
119
|
+
var source = markdown.replacingOccurrences(of: "\r\n", with: "\n")
|
|
120
|
+
switch syntax {
|
|
121
|
+
case .inlineOnlyPreservingWhitespace:
|
|
122
|
+
return merge(flatten(parse(source, keepWhitespace: true)))
|
|
123
|
+
case .inlineOnly:
|
|
124
|
+
return merge(flatten(parse(source, keepWhitespace: false)))
|
|
125
|
+
case .full:
|
|
126
|
+
// Paragraphs: runs of lines between blank lines, concatenated.
|
|
127
|
+
if source.hasSuffix("\n") { source.removeLast() }
|
|
128
|
+
var paragraphs: [String] = []
|
|
129
|
+
var current: [Substring] = []
|
|
130
|
+
for line in source.split(separator: "\n", omittingEmptySubsequences: false) {
|
|
131
|
+
if line.allSatisfy({ $0 == " " || $0 == "\t" }) {
|
|
132
|
+
if !current.isEmpty { paragraphs.append(current.joined(separator: "\n")) }
|
|
133
|
+
current = []
|
|
134
|
+
} else {
|
|
135
|
+
current.append(line)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if !current.isEmpty { paragraphs.append(current.joined(separator: "\n")) }
|
|
139
|
+
return merge(paragraphs.flatMap { flatten(parse($0, keepWhitespace: false)) })
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// MARK: Flattening
|
|
144
|
+
|
|
145
|
+
static func flatten(_ nodes: [Node], intent: InlinePresentationIntent = [], link: String? = nil, image: String? = nil) -> [Segment] {
|
|
146
|
+
nodes.flatMap { node -> [Segment] in
|
|
147
|
+
switch node {
|
|
148
|
+
case .text(let text): [Segment(text: text, intent: intent, link: link, image: image)]
|
|
149
|
+
case .code(let text): [Segment(text: text, intent: intent.union(.code), link: link, image: image)]
|
|
150
|
+
case .lineBreak: [Segment(text: "\n", intent: intent.union(.lineBreak), link: link, image: image)]
|
|
151
|
+
case .styled(let style, let children): flatten(children, intent: intent.union(style), link: link, image: image)
|
|
152
|
+
case .link(let url, let children): flatten(children, intent: intent, link: url, image: image)
|
|
153
|
+
case .image(let url, let children): flatten(children, intent: intent, link: link, image: url)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Joins neighbors with the same attributes, and drops empty ones.
|
|
159
|
+
static func merge(_ segments: [Segment]) -> [Segment] {
|
|
160
|
+
var out: [Segment] = []
|
|
161
|
+
for segment in segments where !segment.text.isEmpty {
|
|
162
|
+
if var last = out.last, last.intent == segment.intent, last.link == segment.link, last.image == segment.image {
|
|
163
|
+
last.text += segment.text
|
|
164
|
+
out[out.count - 1] = last
|
|
165
|
+
} else {
|
|
166
|
+
out.append(segment)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return out
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// MARK: Parsing
|
|
173
|
+
|
|
174
|
+
/// A piece of the inline content while it is parsed: finished nodes, and
|
|
175
|
+
/// the delimiters that may yet open or close emphasis or a link.
|
|
176
|
+
enum Piece {
|
|
177
|
+
case node(Node)
|
|
178
|
+
case delimiter(Delimiter)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
struct Delimiter {
|
|
182
|
+
var char: Character
|
|
183
|
+
var count: Int
|
|
184
|
+
var originalCount: Int
|
|
185
|
+
var canOpen: Bool
|
|
186
|
+
var canClose: Bool
|
|
187
|
+
/// `[` or `![`: a link or image opener.
|
|
188
|
+
var isBracket: Bool { char == "[" || char == "!" }
|
|
189
|
+
var active = true
|
|
190
|
+
|
|
191
|
+
var literal: String { char == "!" ? "![" : String(repeating: char, count: count) }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static let punctuation = Set("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
|
|
195
|
+
|
|
196
|
+
static func isPunctuation(_ c: Character?) -> Bool {
|
|
197
|
+
guard let c else { return false }
|
|
198
|
+
return punctuation.contains(c) || c.unicodeScalars.allSatisfy { CharacterSet.punctuationCharacters.contains($0) || CharacterSet.symbols.contains($0) }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static func isWhitespace(_ c: Character?) -> Bool {
|
|
202
|
+
guard let c else { return true }
|
|
203
|
+
return c.isWhitespace
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
static func parse(_ source: String, keepWhitespace: Bool) -> [Node] {
|
|
207
|
+
let chars = Array(source)
|
|
208
|
+
var pieces: [Piece] = []
|
|
209
|
+
var text = ""
|
|
210
|
+
var i = 0
|
|
211
|
+
|
|
212
|
+
func flushText() {
|
|
213
|
+
if !text.isEmpty { pieces.append(.node(.text(text))); text = "" }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
while i < chars.count {
|
|
217
|
+
let c = chars[i]
|
|
218
|
+
switch c {
|
|
219
|
+
case "\\":
|
|
220
|
+
if i + 1 < chars.count, chars[i + 1] == "\n" {
|
|
221
|
+
flushText()
|
|
222
|
+
pieces.append(.node(.lineBreak))
|
|
223
|
+
i += 2
|
|
224
|
+
} else if i + 1 < chars.count, punctuation.contains(chars[i + 1]) {
|
|
225
|
+
text.append(chars[i + 1])
|
|
226
|
+
i += 2
|
|
227
|
+
} else {
|
|
228
|
+
text.append(c)
|
|
229
|
+
i += 1
|
|
230
|
+
}
|
|
231
|
+
case "`":
|
|
232
|
+
var run = 0
|
|
233
|
+
while i + run < chars.count, chars[i + run] == "`" { run += 1 }
|
|
234
|
+
if let end = closingBackticks(chars, from: i + run, count: run) {
|
|
235
|
+
flushText()
|
|
236
|
+
var code = String(chars[(i + run)..<end]).replacingOccurrences(of: "\n", with: " ")
|
|
237
|
+
if code.count >= 2, code.first == " ", code.last == " ", !code.allSatisfy({ $0 == " " }) {
|
|
238
|
+
code = String(code.dropFirst().dropLast())
|
|
239
|
+
}
|
|
240
|
+
pieces.append(.node(.code(code)))
|
|
241
|
+
i = end + run
|
|
242
|
+
} else {
|
|
243
|
+
text += String(repeating: "`", count: run)
|
|
244
|
+
i += run
|
|
245
|
+
}
|
|
246
|
+
case "*", "_", "~":
|
|
247
|
+
var run = 0
|
|
248
|
+
while i + run < chars.count, chars[i + run] == c { run += 1 }
|
|
249
|
+
let before: Character? = i > 0 ? chars[i - 1] : nil
|
|
250
|
+
let after: Character? = i + run < chars.count ? chars[i + run] : nil
|
|
251
|
+
let leftFlanking = !isWhitespace(after) && (!isPunctuation(after) || isWhitespace(before) || isPunctuation(before))
|
|
252
|
+
let rightFlanking = !isWhitespace(before) && (!isPunctuation(before) || isWhitespace(after) || isPunctuation(after))
|
|
253
|
+
var canOpen = leftFlanking
|
|
254
|
+
var canClose = rightFlanking
|
|
255
|
+
if c == "_" {
|
|
256
|
+
canOpen = leftFlanking && (!rightFlanking || isPunctuation(before))
|
|
257
|
+
canClose = rightFlanking && (!leftFlanking || isPunctuation(after))
|
|
258
|
+
}
|
|
259
|
+
if c == "~" && run > 2 {
|
|
260
|
+
text += String(repeating: "~", count: run)
|
|
261
|
+
} else {
|
|
262
|
+
flushText()
|
|
263
|
+
pieces.append(.delimiter(Delimiter(char: c, count: run, originalCount: run, canOpen: canOpen, canClose: canClose)))
|
|
264
|
+
}
|
|
265
|
+
i += run
|
|
266
|
+
case "!" where i + 1 < chars.count && chars[i + 1] == "[":
|
|
267
|
+
flushText()
|
|
268
|
+
pieces.append(.delimiter(Delimiter(char: "!", count: 1, originalCount: 1, canOpen: true, canClose: false)))
|
|
269
|
+
i += 2
|
|
270
|
+
case "[":
|
|
271
|
+
flushText()
|
|
272
|
+
pieces.append(.delimiter(Delimiter(char: "[", count: 1, originalCount: 1, canOpen: true, canClose: false)))
|
|
273
|
+
i += 1
|
|
274
|
+
case "]":
|
|
275
|
+
flushText()
|
|
276
|
+
if let next = closeBracket(&pieces, chars, after: i + 1) {
|
|
277
|
+
i = next
|
|
278
|
+
} else {
|
|
279
|
+
text.append("]")
|
|
280
|
+
i += 1
|
|
281
|
+
}
|
|
282
|
+
case "<":
|
|
283
|
+
if let (url, end) = autolink(chars, from: i) {
|
|
284
|
+
flushText()
|
|
285
|
+
pieces.append(.node(.link(url, [.text(url.hasPrefix("mailto:") && !String(chars[(i + 1)..<end]).hasPrefix("mailto:") ? String(url.dropFirst(7)) : String(chars[(i + 1)..<end]))])))
|
|
286
|
+
i = end + 1
|
|
287
|
+
} else {
|
|
288
|
+
text.append(c)
|
|
289
|
+
i += 1
|
|
290
|
+
}
|
|
291
|
+
case "&":
|
|
292
|
+
if let (decoded, end) = entity(chars, from: i) {
|
|
293
|
+
text += decoded
|
|
294
|
+
i = end
|
|
295
|
+
} else {
|
|
296
|
+
text.append(c)
|
|
297
|
+
i += 1
|
|
298
|
+
}
|
|
299
|
+
case "\n":
|
|
300
|
+
// Two or more spaces before a line break make a hard break.
|
|
301
|
+
if text.hasSuffix(" ") {
|
|
302
|
+
while text.hasSuffix(" ") { text.removeLast() }
|
|
303
|
+
flushText()
|
|
304
|
+
pieces.append(.node(.lineBreak))
|
|
305
|
+
} else if keepWhitespace {
|
|
306
|
+
text.append("\n")
|
|
307
|
+
} else {
|
|
308
|
+
while text.hasSuffix(" ") { text.removeLast() }
|
|
309
|
+
text.append(" ")
|
|
310
|
+
}
|
|
311
|
+
i += 1
|
|
312
|
+
if !keepWhitespace { while i < chars.count, chars[i] == " " { i += 1 } }
|
|
313
|
+
default:
|
|
314
|
+
text.append(c)
|
|
315
|
+
i += 1
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
flushText()
|
|
319
|
+
processEmphasis(&pieces, from: 0)
|
|
320
|
+
return nodes(of: pieces)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// The pieces as nodes, with delimiters that matched nothing as text.
|
|
324
|
+
static func nodes(of pieces: some Collection<Piece>) -> [Node] {
|
|
325
|
+
var out: [Node] = []
|
|
326
|
+
for piece in pieces {
|
|
327
|
+
let node: Node
|
|
328
|
+
switch piece {
|
|
329
|
+
case .node(let n): node = n
|
|
330
|
+
case .delimiter(let d): node = .text(d.literal)
|
|
331
|
+
}
|
|
332
|
+
if case .text(let new) = node, case .text(let old)? = out.last {
|
|
333
|
+
out[out.count - 1] = .text(old + new)
|
|
334
|
+
} else {
|
|
335
|
+
out.append(node)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return out
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
static func closingBackticks(_ chars: [Character], from start: Int, count: Int) -> Int? {
|
|
342
|
+
var j = start
|
|
343
|
+
while j < chars.count {
|
|
344
|
+
if chars[j] == "`" {
|
|
345
|
+
var run = 0
|
|
346
|
+
while j + run < chars.count, chars[j + run] == "`" { run += 1 }
|
|
347
|
+
if run == count { return j }
|
|
348
|
+
j += run
|
|
349
|
+
} else {
|
|
350
|
+
j += 1
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return nil
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// `]` after a `[` or `![` opener: when `(destination "title")` follows,
|
|
357
|
+
/// the pieces since the opener become a link or image.
|
|
358
|
+
static func closeBracket(_ pieces: inout [Piece], _ chars: [Character], after start: Int) -> Int? {
|
|
359
|
+
guard let openerIndex = pieces.lastIndex(where: { if case .delimiter(let d) = $0 { return d.isBracket }; return false }),
|
|
360
|
+
case .delimiter(let opener) = pieces[openerIndex] else { return nil }
|
|
361
|
+
guard opener.active, let (destination, end) = inlineDestination(chars, from: start) else {
|
|
362
|
+
// Not a link: the opener is literal text.
|
|
363
|
+
pieces[openerIndex] = .node(.text(opener.literal))
|
|
364
|
+
return nil
|
|
365
|
+
}
|
|
366
|
+
processEmphasis(&pieces, from: openerIndex + 1)
|
|
367
|
+
let children = nodes(of: pieces[(openerIndex + 1)...])
|
|
368
|
+
pieces.removeSubrange(openerIndex...)
|
|
369
|
+
if opener.char == "!" {
|
|
370
|
+
pieces.append(.node(.image(destination, children)))
|
|
371
|
+
} else {
|
|
372
|
+
pieces.append(.node(.link(destination, children)))
|
|
373
|
+
// No links inside links: earlier openers can no longer make one.
|
|
374
|
+
for index in pieces.indices {
|
|
375
|
+
if case .delimiter(var d) = pieces[index], d.char == "[" {
|
|
376
|
+
d.active = false
|
|
377
|
+
pieces[index] = .delimiter(d)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return end
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/// `(destination "title")` at `start`: the destination and the index after `)`.
|
|
385
|
+
static func inlineDestination(_ chars: [Character], from start: Int) -> (String, Int)? {
|
|
386
|
+
guard start < chars.count, chars[start] == "(" else { return nil }
|
|
387
|
+
var j = start + 1
|
|
388
|
+
func skipSpaces() { while j < chars.count, chars[j] == " " || chars[j] == "\n" || chars[j] == "\t" { j += 1 } }
|
|
389
|
+
skipSpaces()
|
|
390
|
+
var destination = ""
|
|
391
|
+
if j < chars.count, chars[j] == "<" {
|
|
392
|
+
j += 1
|
|
393
|
+
while j < chars.count, chars[j] != ">" {
|
|
394
|
+
if chars[j] == "\n" || chars[j] == "<" { return nil }
|
|
395
|
+
if chars[j] == "\\", j + 1 < chars.count, punctuation.contains(chars[j + 1]) { j += 1 }
|
|
396
|
+
destination.append(chars[j])
|
|
397
|
+
j += 1
|
|
398
|
+
}
|
|
399
|
+
guard j < chars.count else { return nil }
|
|
400
|
+
j += 1
|
|
401
|
+
} else {
|
|
402
|
+
var depth = 0
|
|
403
|
+
while j < chars.count {
|
|
404
|
+
let c = chars[j]
|
|
405
|
+
if c == " " || c == "\n" || c == "\t" { break }
|
|
406
|
+
if c == "(" { depth += 1 }
|
|
407
|
+
if c == ")" {
|
|
408
|
+
if depth == 0 { break }
|
|
409
|
+
depth -= 1
|
|
410
|
+
}
|
|
411
|
+
if c == "\\", j + 1 < chars.count, punctuation.contains(chars[j + 1]) {
|
|
412
|
+
j += 1
|
|
413
|
+
}
|
|
414
|
+
destination.append(chars[j])
|
|
415
|
+
j += 1
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
skipSpaces()
|
|
419
|
+
if j < chars.count, chars[j] == "\"" || chars[j] == "'" || chars[j] == "(" {
|
|
420
|
+
let close: Character = chars[j] == "(" ? ")" : chars[j]
|
|
421
|
+
j += 1
|
|
422
|
+
while j < chars.count, chars[j] != close { j += 1 }
|
|
423
|
+
guard j < chars.count else { return nil }
|
|
424
|
+
j += 1
|
|
425
|
+
skipSpaces()
|
|
426
|
+
}
|
|
427
|
+
guard j < chars.count, chars[j] == ")" else { return nil }
|
|
428
|
+
return (destination, j + 1)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/// `<scheme:…>` or `<user@host>`: the URL and the index of `>`.
|
|
432
|
+
static func autolink(_ chars: [Character], from start: Int) -> (String, Int)? {
|
|
433
|
+
var j = start + 1
|
|
434
|
+
var body = ""
|
|
435
|
+
while j < chars.count, chars[j] != ">" {
|
|
436
|
+
if chars[j] == " " || chars[j] == "<" || chars[j] == "\n" { return nil }
|
|
437
|
+
body.append(chars[j])
|
|
438
|
+
j += 1
|
|
439
|
+
}
|
|
440
|
+
guard j < chars.count else { return nil }
|
|
441
|
+
if let colon = body.firstIndex(of: ":") {
|
|
442
|
+
let scheme = body[..<colon]
|
|
443
|
+
guard (2...32).contains(scheme.count), scheme.first!.isLetter,
|
|
444
|
+
scheme.allSatisfy({ $0.isLetter || $0.isNumber || $0 == "+" || $0 == "." || $0 == "-" }) else { return nil }
|
|
445
|
+
return (body, j)
|
|
446
|
+
}
|
|
447
|
+
if body.contains("@"), !body.hasPrefix("@"), !body.hasSuffix("@") { return ("mailto:" + body, j) }
|
|
448
|
+
return nil
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
static let namedEntities: [String: String] = [
|
|
452
|
+
"amp": "&", "lt": "<", "gt": ">", "quot": "\"", "apos": "'", "nbsp": "\u{00A0}",
|
|
453
|
+
"hellip": "…", "mdash": "—", "ndash": "–", "lsquo": "‘", "rsquo": "’", "ldquo": "“", "rdquo": "”",
|
|
454
|
+
"copy": "©", "reg": "®", "trade": "™", "middot": "·", "bull": "•", "laquo": "«", "raquo": "»",
|
|
455
|
+
]
|
|
456
|
+
|
|
457
|
+
/// `&name;`, `{` or `😀` at `start`: the decoded text and the index after `;`.
|
|
458
|
+
static func entity(_ chars: [Character], from start: Int) -> (String, Int)? {
|
|
459
|
+
guard let semicolon = chars[start...].prefix(34).firstIndex(of: ";"), semicolon > start + 1 else { return nil }
|
|
460
|
+
let body = String(chars[(start + 1)..<semicolon])
|
|
461
|
+
if body.hasPrefix("#x") || body.hasPrefix("#X") {
|
|
462
|
+
guard let code = UInt32(body.dropFirst(2), radix: 16), let scalar = Unicode.Scalar(code), code != 0 else { return nil }
|
|
463
|
+
return (String(Character(scalar)), semicolon + 1)
|
|
464
|
+
}
|
|
465
|
+
if body.hasPrefix("#") {
|
|
466
|
+
guard let code = UInt32(body.dropFirst()), let scalar = Unicode.Scalar(code), code != 0 else { return nil }
|
|
467
|
+
return (String(Character(scalar)), semicolon + 1)
|
|
468
|
+
}
|
|
469
|
+
guard let decoded = namedEntities[body] else { return nil }
|
|
470
|
+
return (decoded, semicolon + 1)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// CommonMark's emphasis algorithm over the pieces from `bottom` on: each
|
|
474
|
+
/// closer, left to right, matches the nearest opener of its kind, and
|
|
475
|
+
/// what lies between becomes emphasized (or struck through).
|
|
476
|
+
static func processEmphasis(_ pieces: inout [Piece], from bottom: Int) {
|
|
477
|
+
var closerIndex = bottom
|
|
478
|
+
while closerIndex < pieces.count {
|
|
479
|
+
guard case .delimiter(var closer) = pieces[closerIndex], !closer.isBracket, closer.canClose else {
|
|
480
|
+
closerIndex += 1
|
|
481
|
+
continue
|
|
482
|
+
}
|
|
483
|
+
var openerIndex: Int?
|
|
484
|
+
var j = closerIndex - 1
|
|
485
|
+
while j >= bottom {
|
|
486
|
+
if case .delimiter(let opener) = pieces[j], opener.char == closer.char, opener.canOpen {
|
|
487
|
+
let matches: Bool
|
|
488
|
+
if closer.char == "~" {
|
|
489
|
+
matches = opener.count == closer.count
|
|
490
|
+
} else if (opener.canClose || closer.canOpen),
|
|
491
|
+
(opener.originalCount + closer.originalCount) % 3 == 0,
|
|
492
|
+
!(opener.originalCount % 3 == 0 && closer.originalCount % 3 == 0) {
|
|
493
|
+
matches = false
|
|
494
|
+
} else {
|
|
495
|
+
matches = true
|
|
496
|
+
}
|
|
497
|
+
if matches { openerIndex = j; break }
|
|
498
|
+
}
|
|
499
|
+
j -= 1
|
|
500
|
+
}
|
|
501
|
+
guard let openerIndex, case .delimiter(var opener) = pieces[openerIndex] else {
|
|
502
|
+
closerIndex += 1
|
|
503
|
+
continue
|
|
504
|
+
}
|
|
505
|
+
let used: Int
|
|
506
|
+
let style: InlinePresentationIntent
|
|
507
|
+
if closer.char == "~" {
|
|
508
|
+
used = closer.count
|
|
509
|
+
style = .strikethrough
|
|
510
|
+
} else {
|
|
511
|
+
used = opener.count >= 2 && closer.count >= 2 ? 2 : 1
|
|
512
|
+
style = used == 2 ? .stronglyEmphasized : .emphasized
|
|
513
|
+
}
|
|
514
|
+
let inner = nodes(of: pieces[(openerIndex + 1)..<closerIndex])
|
|
515
|
+
opener.count -= used
|
|
516
|
+
closer.count -= used
|
|
517
|
+
var replacement: [Piece] = []
|
|
518
|
+
if opener.count > 0 { replacement.append(.delimiter(opener)) }
|
|
519
|
+
replacement.append(.node(.styled(style, inner)))
|
|
520
|
+
let styledIndex = openerIndex + replacement.count - 1
|
|
521
|
+
if closer.count > 0 { replacement.append(.delimiter(closer)) }
|
|
522
|
+
pieces.replaceSubrange(openerIndex...closerIndex, with: replacement)
|
|
523
|
+
// A closer with some left over is next again; otherwise the piece after the run.
|
|
524
|
+
closerIndex = styledIndex + 1
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// `Measurement.formatted()` and `Measurement.FormatStyle`, which corelibs
|
|
2
|
+
// Foundation lacks: the value as `Double.formatted()` writes it, then the
|
|
3
|
+
// unit's symbol ("57°F", "5 km"). A temperature in the general or weather
|
|
4
|
+
// usage is converted to the locale's unit first (Fahrenheit in the United
|
|
5
|
+
// States and the few regions that use it, Celsius elsewhere), as Apple's
|
|
6
|
+
// does; other units are written in their own. Units are written as symbols
|
|
7
|
+
// at every width.
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
extension Measurement {
|
|
12
|
+
public struct FormatStyle: Foundation.FormatStyle, Hashable, Sendable {
|
|
13
|
+
public struct UnitWidth: Codable, Hashable, Sendable {
|
|
14
|
+
let name: String
|
|
15
|
+
public static var wide: UnitWidth { UnitWidth(name: "wide") }
|
|
16
|
+
public static var abbreviated: UnitWidth { UnitWidth(name: "abbreviated") }
|
|
17
|
+
public static var narrow: UnitWidth { UnitWidth(name: "narrow") }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public var width: UnitWidth
|
|
21
|
+
public var locale: Locale
|
|
22
|
+
/// Whether a temperature is converted to the locale's unit.
|
|
23
|
+
var convertsTemperature: Bool
|
|
24
|
+
|
|
25
|
+
public init(width: UnitWidth = .abbreviated, locale: Locale = .autoupdatingCurrent) {
|
|
26
|
+
self.width = width
|
|
27
|
+
self.locale = locale
|
|
28
|
+
convertsTemperature = true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public func locale(_ locale: Locale) -> Self {
|
|
32
|
+
var copy = self
|
|
33
|
+
copy.locale = locale
|
|
34
|
+
return copy
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public func format(_ measurement: Measurement<UnitType>) -> String {
|
|
38
|
+
var value = measurement.value
|
|
39
|
+
var symbol = measurement.unit.symbol
|
|
40
|
+
if convertsTemperature, let temperature = measurement as? Measurement<UnitTemperature> {
|
|
41
|
+
let unit: UnitTemperature = Self.usesFahrenheit(locale) ? .fahrenheit : .celsius
|
|
42
|
+
let converted = temperature.converted(to: unit)
|
|
43
|
+
value = converted.value
|
|
44
|
+
symbol = unit.symbol
|
|
45
|
+
}
|
|
46
|
+
let number = value.formatted()
|
|
47
|
+
// Temperatures and angles hug their number, as in "21°C".
|
|
48
|
+
let hugs = symbol.hasPrefix("°") || symbol == "%" || symbol == "′" || symbol == "″"
|
|
49
|
+
return hugs ? number + symbol : number + " " + symbol
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static func usesFahrenheit(_ locale: Locale) -> Bool {
|
|
53
|
+
["US", "LR", "MM", "BS", "BZ", "KY", "PW", "FM", "MH"].contains(locale.region?.identifier ?? "US")
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// The measurement in the locale's style (see the top of the file).
|
|
58
|
+
public func formatted() -> String {
|
|
59
|
+
FormatStyle().format(self)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public func formatted<S: Foundation.FormatStyle>(_ style: S) -> S.FormatOutput where S.FormatInput == Measurement<UnitType> {
|
|
63
|
+
style.format(self)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
extension Measurement.FormatStyle where UnitType == UnitTemperature {
|
|
68
|
+
/// How a temperature is used: in the general and weather usages it is
|
|
69
|
+
/// converted to the locale's unit; `asProvided` keeps its own.
|
|
70
|
+
public struct Usage: Codable, Hashable, Sendable {
|
|
71
|
+
let converts: Bool
|
|
72
|
+
public static var general: Usage { Usage(converts: true) }
|
|
73
|
+
public static var weather: Usage { Usage(converts: true) }
|
|
74
|
+
public static var person: Usage { Usage(converts: true) }
|
|
75
|
+
public static var asProvided: Usage { Usage(converts: false) }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public init(width: UnitWidth = .abbreviated, locale: Locale = .autoupdatingCurrent, usage: Usage) {
|
|
79
|
+
self.init(width: width, locale: locale)
|
|
80
|
+
convertsTemperature = usage.converts
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
extension Foundation.FormatStyle {
|
|
85
|
+
public static func measurement<UnitType: Dimension>(width: Measurement<UnitType>.FormatStyle.UnitWidth = .abbreviated) -> Self
|
|
86
|
+
where Self == Measurement<UnitType>.FormatStyle {
|
|
87
|
+
Measurement<UnitType>.FormatStyle(width: width)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public static func measurement(width: Measurement<UnitTemperature>.FormatStyle.UnitWidth = .abbreviated,
|
|
91
|
+
usage: Measurement<UnitTemperature>.FormatStyle.Usage) -> Self
|
|
92
|
+
where Self == Measurement<UnitTemperature>.FormatStyle {
|
|
93
|
+
Measurement<UnitTemperature>.FormatStyle(width: width, usage: usage)
|
|
94
|
+
}
|
|
95
|
+
}
|