mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
feat(ios): update dependencies to track upstream repository (#11143)
This commit is contained in:
@@ -46,7 +46,6 @@
|
||||
50802D5E2D112F7D00694021 /* Intelligents */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Intelligents; sourceTree = "<group>"; };
|
||||
50A285D52D112A5E000D5A6D /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
|
||||
50A285D62D112A5E000D5A6D /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
50FEBD7B2D3F7719002847B5 /* ChidoriMenu */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ChidoriMenu; sourceTree = "<group>"; };
|
||||
50FF42892D2E757E0050AA83 /* ApplicationBridgedWindowScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationBridgedWindowScript.swift; sourceTree = "<group>"; };
|
||||
50FF428B2D2E77CC0050AA83 /* AffineViewController+AIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AffineViewController+AIButton.swift"; sourceTree = "<group>"; };
|
||||
9D52FC422D26CDB600105D0A /* JSValueContainerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSValueContainerExt.swift; sourceTree = "<group>"; };
|
||||
@@ -135,7 +134,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5039CC962D1D42C700874F32 /* AffineGraphQL */,
|
||||
50FEBD7B2D3F7719002847B5 /* ChidoriMenu */,
|
||||
50802D5E2D112F7D00694021 /* Intelligents */,
|
||||
);
|
||||
path = Packages;
|
||||
|
||||
@@ -9,6 +9,15 @@
|
||||
"version" : "1.18.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "chidorimenu",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/Lakr233/ChidoriMenu",
|
||||
"state" : {
|
||||
"revision" : "ccd37f01cd4dcf4fb0889f263573d90d5c16e59b",
|
||||
"version" : "2.4.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "splash",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -27,6 +36,15 @@
|
||||
"version" : "0.15.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-cmark",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-cmark",
|
||||
"state" : {
|
||||
"revision" : "3ccff77b2dc5b96b77db3da0d68d28068593fa53",
|
||||
"version" : "0.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@@ -102,9 +102,9 @@ extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApe
|
||||
presentIntoCurrentContext(withTargetController: controller)
|
||||
})
|
||||
}
|
||||
view.present(menu: .init(children: actions)) { menu in
|
||||
menu.overrideUserInterfaceStyle = .dark
|
||||
}
|
||||
view.present(menu: .init(children: actions)) { controller in
|
||||
controller.overrideUserInterfaceStyle = .dark
|
||||
} controllerDidPresent: { _ in }
|
||||
case .summary:
|
||||
let controller = IntelligentsEphemeralActionController(
|
||||
action: .summarize
|
||||
|
||||
@@ -46,6 +46,15 @@ class AFFiNEViewController: CAPBridgeViewController {
|
||||
super.viewDidAppear(animated)
|
||||
navigationController?.setNavigationBarHidden(false, animated: animated)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
super.motionEnded(motion, with: event)
|
||||
if motion == .motionShake {
|
||||
presentIntelligentsButton()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ let package = Package(
|
||||
.library(name: "AffineGraphQL", targets: ["AffineGraphQL"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.18.0"),
|
||||
.package(url: "https://github.com/apollographql/apollo-ios", exact: "1.18.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
@@ -1,410 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
503E58F02D3C0D6B007BD8C6 /* ChidoriMenu in Frameworks */ = {isa = PBXBuildFile; productRef = 503E58EF2D3C0D6B007BD8C6 /* ChidoriMenu */; };
|
||||
503E58F32D3C10BF007BD8C6 /* LookinServer in Frameworks */ = {isa = PBXBuildFile; productRef = 503E58F22D3C10BF007BD8C6 /* LookinServer */; };
|
||||
503E591E2D3D0F1C007BD8C6 /* SPIndicator in Frameworks */ = {isa = PBXBuildFile; productRef = 503E591D2D3D0F1C007BD8C6 /* SPIndicator */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
505109962D3C0D1000F62A71 /* ChidoriMenuExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChidoriMenuExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
505109982D3C0D1000F62A71 /* ChidoriMenuExample */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = ChidoriMenuExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
505109932D3C0D1000F62A71 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
503E58F02D3C0D6B007BD8C6 /* ChidoriMenu in Frameworks */,
|
||||
503E58F32D3C10BF007BD8C6 /* LookinServer in Frameworks */,
|
||||
503E591E2D3D0F1C007BD8C6 /* SPIndicator in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5051098D2D3C0D1000F62A71 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
505109982D3C0D1000F62A71 /* ChidoriMenuExample */,
|
||||
505109972D3C0D1000F62A71 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
505109972D3C0D1000F62A71 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
505109962D3C0D1000F62A71 /* ChidoriMenuExample.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
505109952D3C0D1000F62A71 /* ChidoriMenuExample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 505109A42D3C0D1100F62A71 /* Build configuration list for PBXNativeTarget "ChidoriMenuExample" */;
|
||||
buildPhases = (
|
||||
503E59202D3D34C8007BD8C6 /* Format Source */,
|
||||
505109922D3C0D1000F62A71 /* Sources */,
|
||||
505109932D3C0D1000F62A71 /* Frameworks */,
|
||||
505109942D3C0D1000F62A71 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
505109982D3C0D1000F62A71 /* ChidoriMenuExample */,
|
||||
);
|
||||
name = ChidoriMenuExample;
|
||||
packageProductDependencies = (
|
||||
503E58EF2D3C0D6B007BD8C6 /* ChidoriMenu */,
|
||||
503E58F22D3C10BF007BD8C6 /* LookinServer */,
|
||||
503E591D2D3D0F1C007BD8C6 /* SPIndicator */,
|
||||
);
|
||||
productName = ChidoriMenuExample;
|
||||
productReference = 505109962D3C0D1000F62A71 /* ChidoriMenuExample.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
5051098E2D3C0D1000F62A71 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1620;
|
||||
LastUpgradeCheck = 1620;
|
||||
TargetAttributes = {
|
||||
505109952D3C0D1000F62A71 = {
|
||||
CreatedOnToolsVersion = 16.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 505109912D3C0D1000F62A71 /* Build configuration list for PBXProject "ChidoriMenuExample" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 5051098D2D3C0D1000F62A71;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
503E58F12D3C10BF007BD8C6 /* XCRemoteSwiftPackageReference "LookinServer" */,
|
||||
503E591C2D3D0F1C007BD8C6 /* XCRemoteSwiftPackageReference "SPIndicator" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 505109972D3C0D1000F62A71 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
505109952D3C0D1000F62A71 /* ChidoriMenuExample */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
505109942D3C0D1000F62A71 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
503E59202D3D34C8007BD8C6 /* Format Source */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Format Source";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/zsh;
|
||||
shellScript = "swiftformat . --swiftversion 6.0 --indent 4 || true\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
505109922D3C0D1000F62A71 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
505109A22D3C0D1100F62A71 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
505109A32D3C0D1100F62A71 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
505109A52D3C0D1100F62A71 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = ChidoriMenuExample/ChidoriMenuExample.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 964G86XT2P;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.ChidoriMenuExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
505109A62D3C0D1100F62A71 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = ChidoriMenuExample/ChidoriMenuExample.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 964G86XT2P;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = wiki.qaq.ChidoriMenuExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
505109912D3C0D1000F62A71 /* Build configuration list for PBXProject "ChidoriMenuExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
505109A22D3C0D1100F62A71 /* Debug */,
|
||||
505109A32D3C0D1100F62A71 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
505109A42D3C0D1100F62A71 /* Build configuration list for PBXNativeTarget "ChidoriMenuExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
505109A52D3C0D1100F62A71 /* Debug */,
|
||||
505109A62D3C0D1100F62A71 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
503E58F12D3C10BF007BD8C6 /* XCRemoteSwiftPackageReference "LookinServer" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/QMUI/LookinServer/";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.2.8;
|
||||
};
|
||||
};
|
||||
503E591C2D3D0F1C007BD8C6 /* XCRemoteSwiftPackageReference "SPIndicator" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ivanvorobei/SPIndicator";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.6.5;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
503E58EF2D3C0D6B007BD8C6 /* ChidoriMenu */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = ChidoriMenu;
|
||||
};
|
||||
503E58F22D3C10BF007BD8C6 /* LookinServer */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 503E58F12D3C10BF007BD8C6 /* XCRemoteSwiftPackageReference "LookinServer" */;
|
||||
productName = LookinServer;
|
||||
};
|
||||
503E591D2D3D0F1C007BD8C6 /* SPIndicator */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 503E591C2D3D0F1C007BD8C6 /* XCRemoteSwiftPackageReference "SPIndicator" */;
|
||||
productName = SPIndicator;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 5051098E2D3C0D1000F62A71 /* Project object */;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:ChidoriMenuExample.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:../">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,394 +0,0 @@
|
||||
//
|
||||
// App.swift
|
||||
// ChidoriMenuExample
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import ChidoriMenu
|
||||
import SPIndicator
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct ChidoriMenuExampleApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Content()
|
||||
.ignoresSafeArea()
|
||||
.navigationTitle("Chidori Menu")
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
|
||||
struct Content: UIViewControllerRepresentable {
|
||||
class ContentController: UIViewController, UITableViewDelegate, UITableViewDataSource {
|
||||
let tableView = UITableView(frame: .zero, style: .insetGrouped)
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.tableFooterView = FooterButton()
|
||||
view.addSubview(tableView)
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
tableView.frame = view.bounds
|
||||
}
|
||||
|
||||
struct Menu {
|
||||
let title: String
|
||||
let menu: UIMenu
|
||||
}
|
||||
|
||||
var firstMenu: Menu = .init(title: "Show Menu Set", menu: .init(children: [
|
||||
UIAction(
|
||||
title: "Copy",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
state: .on
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled,
|
||||
state: .off
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive,
|
||||
state: .mixed
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]))
|
||||
|
||||
var secondMenu: Menu = .init(title: "Show Nested Menu Set", menu: .init(title: "Root Menu", children: [
|
||||
UIAction(
|
||||
title: "Copy",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
state: .on
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIMenu(
|
||||
title: "Child Menu",
|
||||
image: .init(systemName: "menucard"),
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIMenu(
|
||||
title: "Child Menu",
|
||||
image: .init(systemName: "menucard"),
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
title: "Inline Menu",
|
||||
image: UIImage(systemName: "arrow.right"),
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
]))
|
||||
|
||||
var veryLongMenu: Menu = .init(title: "Show Looong Menu Set", menu: .init(title: "Root Menu", children: [
|
||||
UIAction(
|
||||
title: "Copy 1",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 1",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 1",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
UIMenu(
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 2",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 2",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 2",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 3",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 3",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 3",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
title: "Submenu Here",
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 4",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 4",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 4",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
title: "Hello World",
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 5",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 5",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 5",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 6",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 6",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 6",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
UIMenu(
|
||||
options: [.displayInline],
|
||||
children: [
|
||||
UIAction(
|
||||
title: "Copy 7",
|
||||
image: UIImage(systemName: "doc.on.doc")
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Copied", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Paste 7",
|
||||
image: UIImage(systemName: "doc.on.doc"),
|
||||
attributes: .disabled
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Pasted", preset: .done).present()
|
||||
},
|
||||
UIAction(
|
||||
title: "Delete 7",
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive
|
||||
) { _ in
|
||||
SPIndicatorView(title: "Delete", preset: .done).present()
|
||||
},
|
||||
]
|
||||
),
|
||||
]))
|
||||
|
||||
var menuList: [Menu] { [
|
||||
firstMenu,
|
||||
secondMenu,
|
||||
veryLongMenu,
|
||||
] }
|
||||
|
||||
func numberOfSections(in _: UITableView) -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
|
||||
menuList.count
|
||||
}
|
||||
|
||||
func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
|
||||
let menu = menuList[indexPath.row]
|
||||
cell.textLabel?.text = menu.title
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
guard let cell = tableView.cellForRow(at: indexPath) else { return }
|
||||
let anchorView = UIView()
|
||||
cell.addSubview(anchorView)
|
||||
anchorView.frame = .init(
|
||||
x: cell.bounds.midX,
|
||||
y: cell.bounds.midY,
|
||||
width: 0,
|
||||
height: 0
|
||||
)
|
||||
let menu = menuList[indexPath.row].menu
|
||||
anchorView.present(menu: menu)
|
||||
anchorView.removeFromSuperview()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection _: Int) -> UIView? {
|
||||
let view = UIView()
|
||||
view.frame = .init(x: 0, y: 0, width: tableView.bounds.width, height: 20)
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
func makeUIViewController(context _: Context) -> ContentController {
|
||||
ContentController()
|
||||
}
|
||||
|
||||
func updateUIViewController(_: ContentController, context _: Context) {}
|
||||
}
|
||||
|
||||
class FooterButton: UIButton {
|
||||
init() {
|
||||
super.init(frame: .init(x: 0, y: 0, width: 200, height: 44))
|
||||
setTitle("Test Menu", for: .normal)
|
||||
setTitleColor(.systemBlue, for: .normal)
|
||||
titleLabel?.font = .preferredFont(forTextStyle: .footnote)
|
||||
|
||||
interactions = [UIContextMenuInteraction(delegate: self)]
|
||||
|
||||
addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
@objc func buttonTapped() {
|
||||
presentMenu()
|
||||
}
|
||||
|
||||
override func contextMenuInteraction(_: UIContextMenuInteraction, configurationForMenuAtLocation _: CGPoint) -> UIContextMenuConfiguration? {
|
||||
.init(identifier: nil, previewProvider: nil) { items in
|
||||
.init(title: "Hello World", children: items + [UIAction(title: "Action A") { _ in
|
||||
SPIndicator.present(title: "Action A", haptic: .success)
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Christian Selig & Lakr Aream
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,18 +0,0 @@
|
||||
// swift-tools-version: 5.8
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "ChidoriMenu",
|
||||
platforms: [.iOS(.v15), .macCatalyst(.v15)],
|
||||
products: [
|
||||
.library(
|
||||
name: "ChidoriMenu",
|
||||
targets: ["ChidoriMenu"]
|
||||
),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "ChidoriMenu"),
|
||||
]
|
||||
)
|
||||
@@ -1,46 +0,0 @@
|
||||
# ChidoriMenu 🐦⚡️
|
||||
|
||||
A seamless drop-in replacement for UIMenu & UIAction, featuring nested menu support and dark mode compatibility.
|
||||
|
||||
The inspiration behind this project stems from Apple's lack of a unified interface across iOS and Mac Catalyst apps, as well as the absence of a built-in menu presentation method. To address these gaps—and to enable greater customization—we developed ChidoriMenu.
|
||||
|
||||
This project draws heavily on code from [ChidoriMenu](https://github.com/christianselig/ChidoriMenu), and as such, we adhere to the same license.
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- [x] Added support for UIMenu & UIAction
|
||||
- [x] Added drop-in replacement for `_presentMenuAtLocation:` (not recommended for general use)
|
||||
- [x] Added support for nested menus in child elements
|
||||
- [x] Added support for the `.displayInline` menu option
|
||||
- [x] Added compatibility with dark mode
|
||||
- [x] Fixed scrolling issues during selection
|
||||
- [x] Fixed multiple actions triggering simultaneously
|
||||
- [ ] Added support for UIDeferredMenuElement (Contributions Welcome!)
|
||||
|
||||
## Requirements
|
||||
|
||||
- iOS 15.0 or later
|
||||
- macCatalyst 15.0 or later
|
||||
|
||||
## Usage
|
||||
|
||||
Getting started is straightforward. While the interface differs slightly from Apple's menu implementation, the core principles remain the same. Mac Catalyst is fully supported, though it does not bridge to AppKit menus—it functions identically to iOS.
|
||||
|
||||
```swift
|
||||
UIButton.presentMenu()
|
||||
UIView.present(menu: menu)
|
||||
```
|
||||
|
||||
For detailed examples, check out the example project included in the repository.
|
||||
|
||||
## License
|
||||
|
||||
ChidoriMenu is available under the MIT license. See the LICENSE file for more info.
|
||||
|
||||
---
|
||||
|
||||
2025.1.20 - Made with ❤️ by Lakr233
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,114 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+Cell.swift
|
||||
// Chidori
|
||||
//
|
||||
// Created by Christian Selig on 2021-02-16.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu {
|
||||
class Cell: UITableViewCell {
|
||||
var menuTitle: String = "" {
|
||||
didSet { textLabel?.text = menuTitle }
|
||||
}
|
||||
|
||||
var isDestructive: Bool = false {
|
||||
didSet {
|
||||
let color: UIColor = isDestructive ? .systemRed : .label
|
||||
textLabel?.textColor = color
|
||||
imageView?.tintColor = color
|
||||
}
|
||||
}
|
||||
|
||||
var iconImage: UIImage? {
|
||||
didSet { imageView?.image = iconImage }
|
||||
}
|
||||
|
||||
override var accessibilityHint: String? {
|
||||
get { super.accessibilityHint ?? menuTitle }
|
||||
set { super.accessibilityHint = newValue }
|
||||
}
|
||||
|
||||
let sep = UIView()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
backgroundColor = .clear
|
||||
selectionStyle = .none
|
||||
accessibilityTraits = [.button]
|
||||
textLabel?.textColor = .label
|
||||
imageView?.contentMode = .scaleAspectFit
|
||||
imageView?.tintColor = .label
|
||||
accessoryView?.isUserInteractionEnabled = false
|
||||
preservesSuperviewLayoutMargins = false
|
||||
separatorInset = UIEdgeInsets.zero
|
||||
layoutMargins = UIEdgeInsets.zero
|
||||
sep.backgroundColor = ChidoriMenu.dimmingSectionSepratorColor
|
||||
contentView.addSubview(sep)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) { fatalError() }
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
sep.frame = .init(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: bounds.width,
|
||||
height: 1
|
||||
)
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
backgroundColor = selected ? Self.highlightCoverColor : .clear
|
||||
}
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
super.setSelected(highlighted, animated: animated)
|
||||
backgroundColor = highlighted ? Self.highlightCoverColor : .clear
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderCell: UIView {
|
||||
let titleLabel: UILabel = .init()
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
|
||||
titleLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
titleLabel.numberOfLines = 1
|
||||
titleLabel.textColor = .secondaryLabel
|
||||
titleLabel.textAlignment = .left
|
||||
addSubview(titleLabel)
|
||||
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
titleLabel.leadingAnchor.constraint(
|
||||
equalTo: leadingAnchor,
|
||||
constant: 8
|
||||
),
|
||||
titleLabel.trailingAnchor.constraint(
|
||||
equalTo: trailingAnchor,
|
||||
constant: -8
|
||||
),
|
||||
titleLabel.centerYAnchor.constraint(
|
||||
equalTo: centerYAnchor,
|
||||
constant: -ChidoriMenu.sectionTopPadding / 2
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+Constant.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu {
|
||||
static let width: CGFloat = 256
|
||||
static let cornerRadius: CGFloat = 14
|
||||
static let shadowRadius: CGFloat = cornerRadius
|
||||
static let sectionTopPadding: CGFloat = 6
|
||||
static let offsetY: CGFloat = 10
|
||||
|
||||
static let dimmingBackgroundColor = UIColor.black.withAlphaComponent(0.2)
|
||||
static let dimmingSectionSepratorColor = UIColor.black.withAlphaComponent(0.2)
|
||||
static let dimmingSectionSepratorHeight: CGFloat = 4
|
||||
|
||||
static let stackScaleFactor: CGFloat = 0.05
|
||||
}
|
||||
|
||||
extension ChidoriMenu.Cell {
|
||||
static let highlightCoverColor = UIColor.black.withAlphaComponent(0.1)
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+DataSource.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu {
|
||||
struct MenuSection: Identifiable, Hashable {
|
||||
let id = UUID()
|
||||
let title: String
|
||||
}
|
||||
|
||||
struct MenuContent: Identifiable, Hashable {
|
||||
let id = UUID()
|
||||
let content: Content
|
||||
enum Content {
|
||||
case action(UIAction)
|
||||
case submenu(UIMenu)
|
||||
// case deferred(([UIMenuElement]) -> ())
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
|
||||
static func == (lhs: ChidoriMenu.MenuContent, rhs: ChidoriMenu.MenuContent) -> Bool {
|
||||
lhs.hashValue == rhs.hashValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChidoriMenu.MenuContent {
|
||||
var description: String {
|
||||
switch content {
|
||||
case let .action(action):
|
||||
"action: \(action.title)"
|
||||
case let .submenu(menu):
|
||||
"submenu: \(menu.title)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChidoriMenu {
|
||||
typealias DataSource = UITableViewDiffableDataSource<MenuSection, MenuContent>
|
||||
typealias Snapshot = NSDiffableDataSourceSnapshot<MenuSection, MenuContent>
|
||||
|
||||
func updateSnapshot() {
|
||||
var snapshot = Snapshot()
|
||||
let contents = flatMap(menu: menu)
|
||||
for (section, items) in contents {
|
||||
snapshot.appendSections([section])
|
||||
snapshot.appendItems(items, toSection: section)
|
||||
}
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
}
|
||||
|
||||
func flatMap(menu: UIMenu) -> [(MenuSection, [MenuContent])] {
|
||||
var result: [(MenuSection, [MenuContent])] = []
|
||||
|
||||
var sectionTitle: String = menu.title
|
||||
var sectionBuilder: [MenuContent] = []
|
||||
|
||||
let sectionBuilderCommit = {
|
||||
defer {
|
||||
sectionTitle = ""
|
||||
sectionBuilder = []
|
||||
}
|
||||
guard !sectionBuilder.isEmpty else { return }
|
||||
let section = MenuSection(title: sectionTitle)
|
||||
result.append((section, sectionBuilder))
|
||||
}
|
||||
|
||||
for element in menu.children {
|
||||
if let action = element as? UIAction {
|
||||
sectionBuilder.append(.init(content: .action(action)))
|
||||
continue
|
||||
}
|
||||
if let childMenu = element as? UIMenu {
|
||||
if childMenu.options.contains(.displayInline) {
|
||||
sectionBuilderCommit()
|
||||
for (section, items) in flatMap(menu: childMenu) {
|
||||
result.append((section, items))
|
||||
}
|
||||
continue
|
||||
}
|
||||
sectionBuilder.append(.init(content: .submenu(childMenu)))
|
||||
continue
|
||||
}
|
||||
if let deferred = element as? UIDeferredMenuElement {
|
||||
// TODO: IMPL
|
||||
assertionFailure("current \(deferred) unsupported")
|
||||
}
|
||||
assertionFailure("unknown menu element")
|
||||
}
|
||||
sectionBuilderCommit()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func executeAction(_ indexPath: IndexPath) {
|
||||
guard let action = dataSource.itemIdentifier(for: indexPath) else { return }
|
||||
guard let cell = tableView.cellForRow(at: indexPath) else { return }
|
||||
for indexPath in tableView.indexPathsForSelectedRows ?? [] {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
let content = action.content
|
||||
switch content {
|
||||
case let .action(action):
|
||||
action.execute()
|
||||
presentingParent?.dismiss(animated: true)
|
||||
case let .submenu(menu):
|
||||
cell.present(menu: menu, anchorPoint: .init(
|
||||
x: cell.convert(cell.bounds, to: cell.window ?? .init()).midX,
|
||||
y: cell.convert(.zero, to: cell.window ?? .init()).minY - ChidoriMenu.offsetY
|
||||
))
|
||||
iterateMenusInStack {
|
||||
$0.menuStackScaleFactor *= 1 - ChidoriMenu.stackScaleFactor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+Gesture.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu {
|
||||
@objc func panned(panGestureRecognizer: UIPanGestureRecognizer) {
|
||||
let offsetInTableView = panGestureRecognizer.location(in: tableView)
|
||||
guard let indexPath = tableView.indexPathForRow(at: offsetInTableView) else {
|
||||
// If we pan outside the table and there's a cell selected, unselect it
|
||||
for indexPath in tableView.indexPathsForSelectedRows ?? [] {
|
||||
tableView.deselectRow(at: indexPath, animated: false)
|
||||
}
|
||||
return
|
||||
}
|
||||
if panGestureRecognizer.state == .ended {
|
||||
// Treat is as a tap
|
||||
for indexPath in tableView.indexPathsForSelectedRows ?? [] {
|
||||
tableView.deselectRow(at: indexPath, animated: false)
|
||||
}
|
||||
executeAction(indexPath)
|
||||
} else {
|
||||
// This API always confuses me, it does not *select* the cell in a way that would call `didSelectRowAtIndexPath`, this just visually highlights it!
|
||||
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+TableView.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu: UITableViewDelegate {
|
||||
func cell(forRowAtIndex indexPath: IndexPath, dataSource: DataSource) -> UITableViewCell? {
|
||||
guard let section = dataSource.sectionIdentifier(for: indexPath.section),
|
||||
let item = dataSource.itemIdentifier(for: indexPath)
|
||||
else { return nil }
|
||||
let cell = tableView.dequeueReusableCell(
|
||||
withIdentifier: String(describing: Cell.self),
|
||||
for: indexPath
|
||||
) as! Cell
|
||||
switch item.content {
|
||||
case let .action(action):
|
||||
cell.menuTitle = action.title
|
||||
cell.iconImage = action.image
|
||||
cell.isDestructive = action.attributes.contains(.destructive)
|
||||
switch action.state {
|
||||
case .on: cell.accessoryType = .checkmark
|
||||
case .mixed: cell.accessoryType = .detailButton
|
||||
default: cell.accessoryType = .none
|
||||
}
|
||||
cell.accessoryView?.tintColor = .label
|
||||
case let .submenu(menu):
|
||||
cell.menuTitle = menu.title
|
||||
cell.iconImage = menu.image
|
||||
cell.isDestructive = false
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
cell.accessoryView?.tintColor = .label
|
||||
}
|
||||
if section.title.isEmpty, indexPath.row == 0, indexPath.section == 0 {
|
||||
cell.sep.isHidden = true
|
||||
} else {
|
||||
cell.sep.isHidden = false
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
||||
if section == tableView.numberOfSections - 1 { return nil }
|
||||
let footerView = UIView(frame: .zero)
|
||||
footerView.backgroundColor = ChidoriMenu.dimmingSectionSepratorColor
|
||||
return footerView
|
||||
}
|
||||
|
||||
public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
if section == tableView.numberOfSections - 1 { return 0 }
|
||||
return ChidoriMenu.dimmingSectionSepratorHeight
|
||||
}
|
||||
|
||||
public func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
executeAction(indexPath)
|
||||
}
|
||||
|
||||
public func tableView(_: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
guard let section = dataSource.sectionIdentifier(for: section) else {
|
||||
return 0
|
||||
}
|
||||
if section.title.isEmpty { return 0 }
|
||||
return UIFont.preferredFont(forTextStyle: .footnote).lineHeight + 8
|
||||
}
|
||||
|
||||
public func tableView(_: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
guard let section = dataSource.sectionIdentifier(for: section) else {
|
||||
return nil
|
||||
}
|
||||
if section.title.isEmpty { return nil }
|
||||
let cell = HeaderCell()
|
||||
cell.titleLabel.text = section.title
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu+Transition.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ChidoriMenu: UIViewControllerTransitioningDelegate {
|
||||
public func animationController(
|
||||
forPresented _: UIViewController,
|
||||
presenting _: UIViewController,
|
||||
source _: UIViewController
|
||||
) -> UIViewControllerAnimatedTransitioning? {
|
||||
transitionController = ChidoriAnimationController(type: .presentation)
|
||||
return transitionController
|
||||
}
|
||||
|
||||
public func interactionControllerForPresentation(
|
||||
using _: UIViewControllerAnimatedTransitioning
|
||||
) -> UIViewControllerInteractiveTransitioning? {
|
||||
transitionController
|
||||
}
|
||||
|
||||
public func animationController(
|
||||
forDismissed _: UIViewController
|
||||
) -> UIViewControllerAnimatedTransitioning? {
|
||||
ChidoriAnimationController(type: .dismissal)
|
||||
}
|
||||
|
||||
public func presentationController(
|
||||
forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source _: UIViewController
|
||||
) -> UIPresentationController? {
|
||||
let controller = ChidoriPresentationController(
|
||||
presentedViewController: presented,
|
||||
presenting: presenting
|
||||
)
|
||||
controller.transitionDelegate = self
|
||||
return controller
|
||||
}
|
||||
}
|
||||
|
||||
extension ChidoriMenu: ChidoriPresentationController.Delegate {
|
||||
func didTapOverlayView(_: ChidoriPresentationController) {
|
||||
transitionController?.cancelTransition()
|
||||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
//
|
||||
// ChidoriMenu.swift
|
||||
// Chidori
|
||||
//
|
||||
// Created by Christian Selig on 2021-02-15.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class ChidoriMenu: UIViewController {
|
||||
let tableView: UITableView
|
||||
var dataSource: DataSource!
|
||||
|
||||
let menu: UIMenu
|
||||
let anchorPoint: CGPoint
|
||||
|
||||
let backgroundView = UIView()
|
||||
let shadowView = UIView()
|
||||
let panGestureRecognizer: UIPanGestureRecognizer = .init()
|
||||
|
||||
var transitionController: ChidoriAnimationController?
|
||||
var height: CGFloat {
|
||||
tableView.sizeThatFits(
|
||||
CGSize(
|
||||
width: ChidoriMenu.width,
|
||||
height: CGFloat.greatestFiniteMagnitude
|
||||
)
|
||||
).height.rounded(.up)
|
||||
}
|
||||
|
||||
var presentingParent: UIViewController? {
|
||||
var parent: UIViewController? = presentingViewController
|
||||
while let superMenu = parent as? ChidoriMenu {
|
||||
parent = superMenu.presentingViewController
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
var menuStackScaleFactor: CGFloat = 1.0 {
|
||||
didSet { UIView.animate(
|
||||
withDuration: 0.5,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 1.0,
|
||||
initialSpringVelocity: 0.8
|
||||
) { [self] in
|
||||
let factor = menuStackScaleFactor
|
||||
view.transform = CGAffineTransform(scaleX: factor, y: factor)
|
||||
view.layoutIfNeeded()
|
||||
} }
|
||||
}
|
||||
|
||||
required init(menu: UIMenu, anchorPoint: CGPoint) {
|
||||
self.menu = menu
|
||||
self.anchorPoint = anchorPoint
|
||||
|
||||
tableView = TableView(frame: .zero, style: .plain)
|
||||
|
||||
tableView.register(Cell.self, forCellReuseIdentifier: String(describing: Cell.self))
|
||||
tableView.dataSource = dataSource
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
dataSource = DataSource(
|
||||
tableView: tableView
|
||||
) { [weak self] _, indexPath, _ -> UITableViewCell? in
|
||||
guard let self else { return nil }
|
||||
return cell(forRowAtIndex: indexPath, dataSource: dataSource)
|
||||
}
|
||||
|
||||
modalPresentationStyle = .custom
|
||||
transitioningDelegate = self
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
public required init?(coder _: NSCoder) { fatalError() }
|
||||
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = .clear
|
||||
view.layer.masksToBounds = false
|
||||
|
||||
shadowView.backgroundColor = .systemBackground
|
||||
shadowView.layer.shadowColor = UIColor { provider in
|
||||
switch provider.userInterfaceStyle {
|
||||
case .dark: UIColor.black.withAlphaComponent(0.25)
|
||||
default: UIColor.black.withAlphaComponent(0.1)
|
||||
}
|
||||
}.cgColor
|
||||
shadowView.layer.shadowOpacity = 1
|
||||
shadowView.layer.shadowOffset = .zero
|
||||
shadowView.layer.shadowRadius = 8
|
||||
shadowView.layer.cornerRadius = ChidoriMenu.cornerRadius
|
||||
view.addSubview(shadowView)
|
||||
|
||||
backgroundView.backgroundColor = .init(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
|
||||
backgroundView.layer.masksToBounds = true
|
||||
backgroundView.layer.cornerRadius = ChidoriMenu.cornerRadius
|
||||
backgroundView.layer.cornerCurve = .continuous
|
||||
view.addSubview(backgroundView)
|
||||
|
||||
tableView.separatorInset = .zero
|
||||
tableView.contentInset = .zero
|
||||
tableView.delegate = self
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.allowsMultipleSelection = false
|
||||
tableView.selectionFollowsFocus = true
|
||||
tableView.allowsSelection = true
|
||||
tableView.allowsFocus = false
|
||||
tableView.sectionHeaderTopPadding = Self.sectionTopPadding
|
||||
tableView.verticalScrollIndicatorInsets = UIEdgeInsets(
|
||||
top: ChidoriMenu.cornerRadius,
|
||||
left: 0.0,
|
||||
bottom: ChidoriMenu.cornerRadius,
|
||||
right: 0.0
|
||||
)
|
||||
backgroundView.addSubview(tableView)
|
||||
|
||||
tableView.estimatedSectionHeaderHeight = 0.0
|
||||
tableView.estimatedRowHeight = 0.0
|
||||
tableView.estimatedSectionFooterHeight = 0.0
|
||||
|
||||
panGestureRecognizer.addTarget(
|
||||
self,
|
||||
action: #selector(panned(panGestureRecognizer:))
|
||||
)
|
||||
panGestureRecognizer.cancelsTouchesInView = true
|
||||
tableView.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
updateSnapshot()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
transitionController = nil
|
||||
}
|
||||
|
||||
override public func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
backgroundView.frame = view.bounds
|
||||
let contentFrame = backgroundView.bounds
|
||||
tableView.separatorStyle = .none
|
||||
tableView.frame = .init(
|
||||
x: contentFrame.minX,
|
||||
y: contentFrame.minY,
|
||||
width: contentFrame.width,
|
||||
height: contentFrame.height
|
||||
)
|
||||
|
||||
shadowView.layer.shadowPath = UIBezierPath(
|
||||
roundedRect: .init(
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: ChidoriMenu.width,
|
||||
height: tableView.frame.height
|
||||
),
|
||||
cornerRadius: ChidoriMenu.cornerRadius
|
||||
).cgPath
|
||||
|
||||
let isTableViewNotFullyVisible = tableView.contentSize.height > view.bounds.height
|
||||
tableView.isScrollEnabled = isTableViewNotFullyVisible
|
||||
panGestureRecognizer.isEnabled = !isTableViewNotFullyVisible
|
||||
}
|
||||
|
||||
override public func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||
let superController = presentingViewController as? ChidoriMenu
|
||||
superController?.menuStackScaleFactor = 1.0
|
||||
super.dismiss(animated: flag, completion: completion)
|
||||
}
|
||||
|
||||
func dismissIfEmpty() {
|
||||
var count = 0
|
||||
for idx in 0 ..< dataSource.numberOfSections(in: tableView) {
|
||||
count += dataSource.tableView(tableView, numberOfRowsInSection: idx)
|
||||
}
|
||||
guard count <= 0 else { return }
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
func iterateMenusInStack(_ executing: @escaping (ChidoriMenu) -> Void) {
|
||||
var parent: ChidoriMenu? = self
|
||||
while let currentParent = parent {
|
||||
executing(currentParent)
|
||||
parent = currentParent.presentingViewController as? ChidoriMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Present+UIButton.swift
|
||||
//
|
||||
//
|
||||
// Created by QAQ on 2023/9/16.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIButton {
|
||||
func presentMenu() {
|
||||
guard let menu = retrieveMenu() else { return }
|
||||
present(menu: menu)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIButton {
|
||||
private func retrieveMenu() -> UIMenu? {
|
||||
for interaction in interactions {
|
||||
if let menuInteraction = interaction as? UIContextMenuInteraction,
|
||||
let menuConfig = menuInteraction.delegate?.contextMenuInteraction(
|
||||
menuInteraction,
|
||||
configurationForMenuAtLocation: .zero
|
||||
),
|
||||
let menu = menuConfig.retrieveMenu()
|
||||
{
|
||||
return menu
|
||||
}
|
||||
}
|
||||
if let menuConfig = contextMenuInteraction(
|
||||
.init(delegate: RetrieveMenuDelegate.shared),
|
||||
configurationForMenuAtLocation: .zero
|
||||
), let menu = menuConfig.retrieveMenu() {
|
||||
return menu
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private class RetrieveMenuDelegate: NSObject, UIContextMenuInteractionDelegate {
|
||||
static let shared = RetrieveMenuDelegate()
|
||||
func contextMenuInteraction(
|
||||
_: UIContextMenuInteraction,
|
||||
configurationForMenuAtLocation _: CGPoint
|
||||
) -> UIContextMenuConfiguration? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIContextMenuConfiguration {
|
||||
func retrieveMenu() -> UIMenu? {
|
||||
guard responds(to: NSSelectorFromString(["Provider", "action"].reversed().joined())),
|
||||
let actionProvider = value(forKey: ["Provider", "action", "_"].reversed().joined())
|
||||
else { return nil }
|
||||
typealias ActionProviderBlock = @convention(block) ([UIMenuElement]) -> (UIMenu?)
|
||||
let blockPtr = UnsafeRawPointer(Unmanaged<AnyObject>.passUnretained(actionProvider as AnyObject).toOpaque())
|
||||
let handler = unsafeBitCast(blockPtr, to: ActionProviderBlock.self)
|
||||
return handler([])
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// Present+UIView.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension UIView {
|
||||
func present(menu: UIMenu, anchorPoint: CGPoint? = nil, onMenuCreated: (ChidoriMenu) -> Void = { _ in }) {
|
||||
guard let presenter = parentViewController else { return }
|
||||
let chidoriMenu = ChidoriMenu(
|
||||
menu: menu,
|
||||
anchorPoint: anchorPoint ?? convert(.init(
|
||||
x: bounds.midX,
|
||||
y: bounds.midY
|
||||
), to: window)
|
||||
)
|
||||
onMenuCreated(chidoriMenu)
|
||||
presenter.present(chidoriMenu, animated: true) {
|
||||
chidoriMenu.dismissIfEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// UIAction.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIAction {
|
||||
func execute() {
|
||||
guard responds(to: NSSelectorFromString("handler")),
|
||||
let handler = value(forKey: "_handler")
|
||||
else { return }
|
||||
typealias ActionBlock = @convention(block) (UIAction) -> Void
|
||||
let blockPtr = UnsafeRawPointer(Unmanaged<AnyObject>.passUnretained(handler as AnyObject).toOpaque())
|
||||
let block = unsafeBitCast(blockPtr, to: ActionBlock.self)
|
||||
return block(self)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// UIView.swift
|
||||
//
|
||||
//
|
||||
// Created by QAQ on 2023/9/16.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
var parentViewController: UIViewController? {
|
||||
weak var parentResponder: UIResponder? = self
|
||||
while parentResponder != nil {
|
||||
parentResponder = parentResponder!.next
|
||||
if let viewController = parentResponder as? UIViewController {
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
//
|
||||
// ChidoriAnimationController.swift
|
||||
// Chidori
|
||||
//
|
||||
// Created by Christian Selig on 2021-02-15.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private typealias ChidoriDelegateProtocol = AnyObject
|
||||
& UIViewControllerAnimatedTransitioning
|
||||
& UIViewControllerInteractiveTransitioning
|
||||
|
||||
class ChidoriAnimationController: NSObject, ChidoriDelegateProtocol {
|
||||
enum AnimationControllerType { case presentation, dismissal }
|
||||
|
||||
let type: AnimationControllerType
|
||||
var animator: UIViewPropertyAnimator?
|
||||
|
||||
weak var context: UIViewControllerContextTransitioning?
|
||||
|
||||
init(type: AnimationControllerType) {
|
||||
self.type = type
|
||||
}
|
||||
|
||||
func transitionDuration(
|
||||
using _: UIViewControllerContextTransitioning?
|
||||
) -> TimeInterval {
|
||||
switch type {
|
||||
case .presentation:
|
||||
0.5
|
||||
case .dismissal:
|
||||
0.35
|
||||
}
|
||||
}
|
||||
|
||||
func startInteractiveTransition(
|
||||
_ transitionContext: UIViewControllerContextTransitioning
|
||||
) {
|
||||
context = transitionContext
|
||||
animateTransition(using: transitionContext)
|
||||
}
|
||||
|
||||
func cancelTransition() {
|
||||
guard let context, let animator else { return }
|
||||
context.cancelInteractiveTransition()
|
||||
animator.isReversed = true
|
||||
animator.startAnimation()
|
||||
|
||||
guard type == .presentation else { return }
|
||||
if let presentingController = context.viewController(forKey: .from) {
|
||||
presentingController.view.tintAdjustmentMode = .automatic
|
||||
}
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
let interruptableAnimator = interruptibleAnimator(using: transitionContext)
|
||||
|
||||
switch type {
|
||||
case .presentation:
|
||||
if let menu = transitionContext.viewController(
|
||||
forKey: .to
|
||||
) as? ChidoriMenu { transitionContext.containerView.addSubview(menu.view) }
|
||||
|
||||
if let presentingController = transitionContext.viewController(
|
||||
forKey: .from
|
||||
) { presentingController.view.tintAdjustmentMode = .dimmed }
|
||||
case .dismissal:
|
||||
if let presentingController = transitionContext.viewController(
|
||||
forKey: .to
|
||||
) { presentingController.view.tintAdjustmentMode = .automatic }
|
||||
}
|
||||
|
||||
interruptableAnimator.startAnimation()
|
||||
}
|
||||
|
||||
func interruptibleAnimator(
|
||||
using context: UIViewControllerContextTransitioning
|
||||
) -> UIViewImplicitlyAnimating {
|
||||
if let animator { return animator }
|
||||
|
||||
let duration = transitionDuration(using: context)
|
||||
let propertyAnimator = UIViewPropertyAnimator(
|
||||
duration: duration,
|
||||
timingParameters: UISpringTimingParameters(
|
||||
dampingRatio: 0.8,
|
||||
initialVelocity: .init(dx: 0.8, dy: 0.8)
|
||||
)
|
||||
)
|
||||
|
||||
propertyAnimator.isInterruptible = true
|
||||
propertyAnimator.isUserInteractionEnabled = true
|
||||
|
||||
let isPresenting = type == .presentation
|
||||
|
||||
guard let menu = (
|
||||
isPresenting
|
||||
? context.viewController(forKey: .to)
|
||||
: context.viewController(forKey: .from)
|
||||
) as? ChidoriMenu else {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
let finalFrame = context.finalFrame(for: menu)
|
||||
menu.view.frame = finalFrame
|
||||
|
||||
let translationRequired: CGVector = .init(
|
||||
dx: 0,
|
||||
dy: menu.view.frame.height * -0.1
|
||||
)
|
||||
|
||||
let initialAlpha: CGFloat = isPresenting ? 0.0 : 1.0
|
||||
let finalAlpha: CGFloat = isPresenting ? 1.0 : 0.0
|
||||
|
||||
let transform = CGAffineTransform(
|
||||
translationX: translationRequired.dx,
|
||||
y: translationRequired.dy
|
||||
).scaledBy(x: 0.9, y: 0.9)
|
||||
let initialTransform = isPresenting ? transform : .identity
|
||||
let finalTransform = isPresenting ? .identity : transform
|
||||
|
||||
menu.view.transform = initialTransform
|
||||
.concatenating(menu.view.transform)
|
||||
menu.view.alpha = initialAlpha
|
||||
|
||||
propertyAnimator.addAnimations {
|
||||
menu.view.transform = finalTransform
|
||||
menu.view.alpha = finalAlpha
|
||||
}
|
||||
|
||||
propertyAnimator.addCompletion { _ in
|
||||
context.completeTransition(!context.transitionWasCancelled)
|
||||
self.animator = nil
|
||||
}
|
||||
|
||||
animator = propertyAnimator
|
||||
return propertyAnimator
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
//
|
||||
// ChidoriPresentationController.swift
|
||||
// Chidori
|
||||
//
|
||||
// Created by Christian Selig on 2021-02-15.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ChidoriPresentationController: UIPresentationController {
|
||||
let dimmView: UIView = .init()
|
||||
var minimalEdgeInset: CGFloat = 10
|
||||
|
||||
protocol Delegate: AnyObject {
|
||||
func didTapOverlayView(_ chidoriPresentationController: ChidoriPresentationController)
|
||||
}
|
||||
|
||||
weak var transitionDelegate: Delegate?
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
|
||||
guard let containerView else { return }
|
||||
|
||||
dimmView.translatesAutoresizingMaskIntoConstraints = false
|
||||
dimmView.isUserInteractionEnabled = true
|
||||
dimmView.isAccessibilityElement = true
|
||||
dimmView.accessibilityTraits = .button
|
||||
dimmView.accessibilityHint = NSLocalizedString("Close Menu", comment: "")
|
||||
dimmView.backgroundColor = ChidoriMenu.dimmingBackgroundColor
|
||||
dimmView.alpha = 0.0
|
||||
|
||||
presentingViewController.view.tintAdjustmentMode = .dimmed
|
||||
containerView.addSubview(dimmView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
containerView.leadingAnchor.constraint(equalTo: dimmView.leadingAnchor),
|
||||
containerView.trailingAnchor.constraint(equalTo: dimmView.trailingAnchor),
|
||||
containerView.topAnchor.constraint(equalTo: dimmView.topAnchor),
|
||||
containerView.bottomAnchor.constraint(equalTo: dimmView.bottomAnchor),
|
||||
])
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: nil, action: nil)
|
||||
tapGesture.addTarget(self, action: #selector(dimmViewTapped))
|
||||
dimmView.addGestureRecognizer(tapGesture)
|
||||
|
||||
if let transitionCoordinator = presentingViewController.transitionCoordinator {
|
||||
transitionCoordinator.animate { _ in self.dimmView.alpha = 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
presentingViewController.view.tintAdjustmentMode = .automatic
|
||||
|
||||
if let transitionCoordinator = presentingViewController.transitionCoordinator {
|
||||
transitionCoordinator.animate { _ in self.dimmView.alpha = 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.dismissalTransitionDidEnd(completed)
|
||||
|
||||
if completed { dimmView.removeFromSuperview() }
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let menu = presentedViewController as? ChidoriMenu else {
|
||||
return .zero
|
||||
}
|
||||
var height = menu.height
|
||||
if let heightLimit = containerView?.bounds.height {
|
||||
height = min(height, heightLimit * 0.75)
|
||||
}
|
||||
let menuSize = CGSize(width: ChidoriMenu.width, height: height)
|
||||
let originatingPoint = calculateOriginatingPoint(
|
||||
anchorPoint: menu.anchorPoint,
|
||||
menuSize: menuSize
|
||||
)
|
||||
return CGRect(origin: originatingPoint, size: menuSize)
|
||||
}
|
||||
|
||||
private func calculateOriginatingPoint(
|
||||
anchorPoint: CGPoint,
|
||||
menuSize: CGSize
|
||||
) -> CGPoint {
|
||||
guard let containerView else { return .zero }
|
||||
|
||||
let x: CGFloat = {
|
||||
let requiredMinX = anchorPoint.x - ChidoriMenu.width / 2
|
||||
let maxPossibleX = minimalEdgeInset
|
||||
+ containerView.safeAreaInsets.left
|
||||
let rightMostPermissableXPosition = containerView.bounds.width
|
||||
- minimalEdgeInset
|
||||
- containerView.safeAreaInsets.right
|
||||
- menuSize.width
|
||||
return min(
|
||||
rightMostPermissableXPosition,
|
||||
max(requiredMinX, maxPossibleX)
|
||||
)
|
||||
}()
|
||||
|
||||
let y: CGFloat = {
|
||||
let maxY = anchorPoint.y + menuSize.height + minimalEdgeInset + ChidoriMenu.offsetY
|
||||
let allowedY = containerView.bounds.height - containerView.safeAreaInsets.bottom
|
||||
if maxY < allowedY { return anchorPoint.y + ChidoriMenu.offsetY /* move below a little bit */ }
|
||||
// if not, iOS tries to keep as much in the bottom half of the screen as possible
|
||||
// to be closer to where the thumb normally is, presumably
|
||||
return containerView.bounds.height
|
||||
- minimalEdgeInset
|
||||
- containerView.safeAreaInsets.bottom
|
||||
- menuSize.height
|
||||
}()
|
||||
|
||||
return CGPoint(x: x, y: y)
|
||||
}
|
||||
|
||||
@objc func dimmViewTapped() {
|
||||
transitionDelegate?.didTapOverlayView(self)
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// TableView.swift
|
||||
// ChidoriMenu
|
||||
//
|
||||
// Created by 秋星桥 on 1/19/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class TableView: UITableView {
|
||||
// - (BOOL)allowsHeaderViewsToFloat;
|
||||
// - (BOOL)allowsFooterViewsToFloat;
|
||||
@objc var allowsHeaderViewsToFloat: Bool { false }
|
||||
@objc var allowsFooterViewsToFloat: Bool { false }
|
||||
}
|
||||
@@ -15,17 +15,18 @@ let package = Package(
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../AffineGraphQL"),
|
||||
.package(path: "../ChidoriMenu"),
|
||||
.package(path: "../MarkdownView"),
|
||||
.package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.18.0"),
|
||||
.package(url: "https://github.com/LaunchDarkly/swift-eventsource.git", from: "3.3.0"),
|
||||
.package(url: "https://github.com/apple/swift-collections", from: "1.1.4"),
|
||||
.package(url: "https://github.com/Lakr233/ChidoriMenu", from: "2.4.3"),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "Intelligents", dependencies: [
|
||||
"AffineGraphQL",
|
||||
"ChidoriMenu",
|
||||
"MarkdownView",
|
||||
"ChidoriMenu",
|
||||
.product(name: "Apollo", package: "apollo-ios"),
|
||||
.product(name: "LDSwiftEventSource", package: "swift-eventsource"),
|
||||
.product(name: "OrderedCollections", package: "swift-collections"),
|
||||
|
||||
@@ -33,7 +33,6 @@ public class IntelligentsEphemeralActionController: UIViewController {
|
||||
var chatTask: EventSource?
|
||||
var copilotDocumentStorage: String = "" {
|
||||
didSet {
|
||||
guard copilotDocumentStorage != oldValue else { return }
|
||||
updateDocumentPresentationView()
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
*.pyc
|
||||
|
||||
*~
|
||||
*.bak
|
||||
*.diff
|
||||
*#
|
||||
*.zip
|
||||
bstrlib.txt
|
||||
build
|
||||
cmark.dSYM/*
|
||||
cmark
|
||||
.vscode
|
||||
.DS_Store
|
||||
|
||||
# Testing and benchmark
|
||||
alltests.md
|
||||
progit/
|
||||
bench/benchinput.md
|
||||
test/afl_results/
|
||||
|
||||
# Build directories for SwiftPM and Xcode
|
||||
.swiftpm
|
||||
.build
|
||||
@@ -1,170 +0,0 @@
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-----
|
||||
|
||||
houdini.h, houdini_href_e.c, houdini_html_e.c, houdini_html_u.c
|
||||
|
||||
derive from https://github.com/vmg/houdini (with some modifications)
|
||||
|
||||
Copyright (C) 2012 Vicent Martí
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
buffer.h, buffer.c, chunk.h
|
||||
|
||||
are derived from code (C) 2012 Github, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
utf8.c and utf8.c
|
||||
|
||||
are derived from utf8proc
|
||||
(<http://www.public-software-group.org/utf8proc>),
|
||||
(C) 2009 Public Software Group e. V., Berlin, Germany.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The normalization code in normalize.py was derived from the
|
||||
markdowntest project, Copyright 2013 Karl Dubost:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Karl Dubost
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
-----
|
||||
|
||||
The CommonMark spec (test/spec.txt) is
|
||||
|
||||
Copyright (C) 2014-15 John MacFarlane
|
||||
|
||||
Released under the Creative Commons CC-BY-SA 4.0 license:
|
||||
<http://creativecommons.org/licenses/by-sa/4.0/>.
|
||||
|
||||
-----
|
||||
|
||||
The test software in test/ is
|
||||
|
||||
Copyright (c) 2014, John MacFarlane
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -1,42 +0,0 @@
|
||||
// swift-tools-version:5.9
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
/*
|
||||
|
||||
The main purpose to fork this library is for adding streamed parser support.
|
||||
|
||||
Suppose we add a new function:
|
||||
- cmark_parser *cmark_parser_fork(cmark_parser *parser);
|
||||
|
||||
In this way we can call `cmark_parser_finish` to get the result without redo the entire document.
|
||||
|
||||
But it is over my ability to implement this function. The parser class is too complex to manage.
|
||||
|
||||
*/
|
||||
|
||||
let cSettings: [CSetting] = [
|
||||
.define("CMARK_THREADING"),
|
||||
]
|
||||
|
||||
let package = Package(
|
||||
name: "MarkdownParserCore",
|
||||
products: [
|
||||
.library(name: "MarkdownParserCore", targets: ["cmark-gfm"]),
|
||||
.library(name: "MarkdownParserCoreExtension", targets: ["cmark-gfm-extensions"]),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "cmark-gfm",
|
||||
path: "src",
|
||||
cSettings: cSettings
|
||||
),
|
||||
.target(
|
||||
name: "cmark-gfm-extensions",
|
||||
dependencies: ["cmark-gfm"],
|
||||
path: "extensions",
|
||||
cSettings: cSettings
|
||||
),
|
||||
]
|
||||
)
|
||||
@@ -1,510 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "autolink.h"
|
||||
#include <parser.h>
|
||||
#include <utf8.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define strncasecmp _strnicmp
|
||||
#else
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
static int is_valid_hostchar(const uint8_t *link, size_t link_len) {
|
||||
int32_t ch;
|
||||
int r = cmark_utf8proc_iterate(link, (bufsize_t)link_len, &ch);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
return !cmark_utf8proc_is_space(ch) && !cmark_utf8proc_is_punctuation(ch);
|
||||
}
|
||||
|
||||
static int sd_autolink_issafe(const uint8_t *link, size_t link_len) {
|
||||
static const size_t valid_uris_count = 3;
|
||||
static const char *valid_uris[] = {"http://", "https://", "ftp://"};
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < valid_uris_count; ++i) {
|
||||
size_t len = strlen(valid_uris[i]);
|
||||
|
||||
if (link_len > len && strncasecmp((char *)link, valid_uris[i], len) == 0 &&
|
||||
is_valid_hostchar(link + len, link_len - len))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t autolink_delim(uint8_t *data, size_t link_end) {
|
||||
size_t i;
|
||||
size_t closing = 0;
|
||||
size_t opening = 0;
|
||||
|
||||
for (i = 0; i < link_end; ++i) {
|
||||
const uint8_t c = data[i];
|
||||
if (c == '<') {
|
||||
link_end = i;
|
||||
break;
|
||||
} else if (c == '(') {
|
||||
opening++;
|
||||
} else if (c == ')') {
|
||||
closing++;
|
||||
}
|
||||
}
|
||||
|
||||
while (link_end > 0) {
|
||||
switch (data[link_end - 1]) {
|
||||
case ')':
|
||||
/* Allow any number of matching brackets (as recognised in copen/cclose)
|
||||
* at the end of the URL. If there is a greater number of closing
|
||||
* brackets than opening ones, we remove one character from the end of
|
||||
* the link.
|
||||
*
|
||||
* Examples (input text => output linked portion):
|
||||
*
|
||||
* http://www.pokemon.com/Pikachu_(Electric)
|
||||
* => http://www.pokemon.com/Pikachu_(Electric)
|
||||
*
|
||||
* http://www.pokemon.com/Pikachu_((Electric)
|
||||
* => http://www.pokemon.com/Pikachu_((Electric)
|
||||
*
|
||||
* http://www.pokemon.com/Pikachu_(Electric))
|
||||
* => http://www.pokemon.com/Pikachu_(Electric)
|
||||
*
|
||||
* http://www.pokemon.com/Pikachu_((Electric))
|
||||
* => http://www.pokemon.com/Pikachu_((Electric))
|
||||
*/
|
||||
if (closing <= opening) {
|
||||
return link_end;
|
||||
}
|
||||
closing--;
|
||||
link_end--;
|
||||
break;
|
||||
case '?':
|
||||
case '!':
|
||||
case '.':
|
||||
case ',':
|
||||
case ':':
|
||||
case '*':
|
||||
case '_':
|
||||
case '~':
|
||||
case '\'':
|
||||
case '"':
|
||||
link_end--;
|
||||
break;
|
||||
case ';': {
|
||||
size_t new_end = link_end - 2;
|
||||
|
||||
while (new_end > 0 && cmark_isalpha(data[new_end]))
|
||||
new_end--;
|
||||
|
||||
if (new_end < link_end - 2 && data[new_end] == '&')
|
||||
link_end = new_end;
|
||||
else
|
||||
link_end--;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return link_end;
|
||||
}
|
||||
}
|
||||
|
||||
return link_end;
|
||||
}
|
||||
|
||||
static size_t check_domain(uint8_t *data, size_t size, int allow_short) {
|
||||
size_t i, np = 0, uscore1 = 0, uscore2 = 0;
|
||||
|
||||
/* The purpose of this code is to reject urls that contain an underscore
|
||||
* in one of the last two segments. Examples:
|
||||
*
|
||||
* www.xxx.yyy.zzz autolinked
|
||||
* www.xxx.yyy._zzz not autolinked
|
||||
* www.xxx._yyy.zzz not autolinked
|
||||
* www._xxx.yyy.zzz autolinked
|
||||
*
|
||||
* The reason is that domain names are allowed to include underscores,
|
||||
* but host names are not. See: https://stackoverflow.com/a/2183140
|
||||
*/
|
||||
for (i = 1; i < size - 1; i++) {
|
||||
if (data[i] == '\\' && i < size - 2)
|
||||
i++;
|
||||
if (data[i] == '_')
|
||||
uscore2++;
|
||||
else if (data[i] == '.') {
|
||||
uscore1 = uscore2;
|
||||
uscore2 = 0;
|
||||
np++;
|
||||
} else if (!is_valid_hostchar(data + i, size - i) && data[i] != '-')
|
||||
break;
|
||||
}
|
||||
|
||||
if (uscore1 > 0 || uscore2 > 0) {
|
||||
/* If the url is very long then accept it despite the underscores,
|
||||
* to avoid quadratic behavior causing a denial of service. See:
|
||||
* https://github.com/github/cmark-gfm/security/advisories/GHSA-29g3-96g3-jg6c
|
||||
* Reasonable urls are unlikely to have more than 10 segments, so
|
||||
* this extra condition shouldn't have any impact on normal usage.
|
||||
*/
|
||||
if (np <= 10) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_short) {
|
||||
/* We don't need a valid domain in the strict sense (with
|
||||
* least one dot; so just make sure it's composed of valid
|
||||
* domain characters and return the length of the the valid
|
||||
* sequence. */
|
||||
return i;
|
||||
} else {
|
||||
/* a valid domain needs to have at least a dot.
|
||||
* that's as far as we get */
|
||||
return np ? i : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static cmark_node *www_match(cmark_parser *parser, cmark_node *parent,
|
||||
cmark_inline_parser *inline_parser) {
|
||||
cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser);
|
||||
size_t max_rewind = cmark_inline_parser_get_offset(inline_parser);
|
||||
uint8_t *data = chunk->data + max_rewind;
|
||||
size_t size = chunk->len - max_rewind;
|
||||
int start = cmark_inline_parser_get_column(inline_parser);
|
||||
|
||||
size_t link_end;
|
||||
|
||||
if (max_rewind > 0 && strchr("*_~(", data[-1]) == NULL &&
|
||||
!cmark_isspace(data[-1]))
|
||||
return 0;
|
||||
|
||||
if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
|
||||
return 0;
|
||||
|
||||
link_end = check_domain(data, size, 0);
|
||||
|
||||
if (link_end == 0)
|
||||
return NULL;
|
||||
|
||||
while (link_end < size && !cmark_isspace(data[link_end]) && data[link_end] != '<')
|
||||
link_end++;
|
||||
|
||||
link_end = autolink_delim(data, link_end);
|
||||
|
||||
if (link_end == 0)
|
||||
return NULL;
|
||||
|
||||
cmark_inline_parser_set_offset(inline_parser, (int)(max_rewind + link_end));
|
||||
|
||||
cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
|
||||
|
||||
cmark_strbuf buf;
|
||||
cmark_strbuf_init(parser->mem, &buf, 10);
|
||||
cmark_strbuf_puts(&buf, "http://");
|
||||
cmark_strbuf_put(&buf, data, (bufsize_t)link_end);
|
||||
node->as.link.url = cmark_chunk_buf_detach(&buf);
|
||||
|
||||
cmark_node *text = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
|
||||
text->as.literal =
|
||||
cmark_chunk_dup(chunk, (bufsize_t)max_rewind, (bufsize_t)link_end);
|
||||
cmark_node_append_child(node, text);
|
||||
|
||||
node->start_line = text->start_line =
|
||||
node->end_line = text->end_line =
|
||||
cmark_inline_parser_get_line(inline_parser);
|
||||
|
||||
node->start_column = text->start_column = start - 1;
|
||||
node->end_column = text->end_column = cmark_inline_parser_get_column(inline_parser) - 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static cmark_node *url_match(cmark_parser *parser, cmark_node *parent,
|
||||
cmark_inline_parser *inline_parser) {
|
||||
size_t link_end, domain_len;
|
||||
int rewind = 0;
|
||||
|
||||
cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser);
|
||||
int max_rewind = cmark_inline_parser_get_offset(inline_parser);
|
||||
uint8_t *data = chunk->data + max_rewind;
|
||||
size_t size = chunk->len - max_rewind;
|
||||
|
||||
if (size < 4 || data[1] != '/' || data[2] != '/')
|
||||
return 0;
|
||||
|
||||
while (rewind < max_rewind && cmark_isalpha(data[-rewind - 1]))
|
||||
rewind++;
|
||||
|
||||
if (!sd_autolink_issafe(data - rewind, size + rewind))
|
||||
return 0;
|
||||
|
||||
link_end = strlen("://");
|
||||
|
||||
domain_len = check_domain(data + link_end, size - link_end, 1);
|
||||
|
||||
if (domain_len == 0)
|
||||
return 0;
|
||||
|
||||
link_end += domain_len;
|
||||
while (link_end < size && !cmark_isspace(data[link_end]) && data[link_end] != '<')
|
||||
link_end++;
|
||||
|
||||
link_end = autolink_delim(data, link_end);
|
||||
|
||||
if (link_end == 0)
|
||||
return NULL;
|
||||
|
||||
cmark_inline_parser_set_offset(inline_parser, (int)(max_rewind + link_end));
|
||||
cmark_node_unput(parent, rewind);
|
||||
|
||||
cmark_node *node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
|
||||
|
||||
cmark_chunk url = cmark_chunk_dup(chunk, max_rewind - rewind,
|
||||
(bufsize_t)(link_end + rewind));
|
||||
node->as.link.url = url;
|
||||
|
||||
cmark_node *text = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
|
||||
text->as.literal = url;
|
||||
cmark_node_append_child(node, text);
|
||||
|
||||
node->start_line = text->start_line = node->end_line = text->end_line = cmark_inline_parser_get_line(inline_parser);
|
||||
|
||||
node->start_column = text->start_column = max_rewind - rewind;
|
||||
node->end_column = text->end_column = cmark_inline_parser_get_column(inline_parser) - 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
|
||||
cmark_node *parent, unsigned char c,
|
||||
cmark_inline_parser *inline_parser) {
|
||||
if (cmark_inline_parser_in_bracket(inline_parser, false) ||
|
||||
cmark_inline_parser_in_bracket(inline_parser, true))
|
||||
return NULL;
|
||||
|
||||
if (c == ':')
|
||||
return url_match(parser, parent, inline_parser);
|
||||
|
||||
if (c == 'w')
|
||||
return www_match(parser, parent, inline_parser);
|
||||
|
||||
return NULL;
|
||||
|
||||
// note that we could end up re-consuming something already a
|
||||
// part of an inline, because we don't track when the last
|
||||
// inline was finished in inlines.c.
|
||||
}
|
||||
|
||||
static bool validate_protocol(const char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
|
||||
size_t len = strlen(protocol);
|
||||
|
||||
if (len > (max_rewind - rewind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the protocol matches
|
||||
if (memcmp(data - rewind - len, protocol, len) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len == (max_rewind - rewind)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char prev_char = data[-((ptrdiff_t)rewind) - len - 1];
|
||||
|
||||
// Make sure the character before the protocol is non-alphanumeric
|
||||
return !cmark_isalnum(prev_char);
|
||||
}
|
||||
|
||||
static void postprocess_text(cmark_parser *parser, cmark_node *text) {
|
||||
size_t start = 0;
|
||||
size_t offset = 0;
|
||||
// `text` is going to be split into a list of nodes containing shorter segments
|
||||
// of text, so we detach the memory buffer from text and use `cmark_chunk_dup` to
|
||||
// create references to it. Later, `cmark_chunk_to_cstr` is used to convert
|
||||
// the references into allocated buffers. The detached buffer is freed before we
|
||||
// return.
|
||||
cmark_chunk detached_chunk = text->as.literal;
|
||||
text->as.literal = cmark_chunk_dup(&detached_chunk, 0, detached_chunk.len);
|
||||
|
||||
uint8_t *data = text->as.literal.data;
|
||||
size_t remaining = text->as.literal.len;
|
||||
|
||||
while (true) {
|
||||
size_t link_end;
|
||||
uint8_t *at;
|
||||
bool auto_mailto = true;
|
||||
bool is_xmpp = false;
|
||||
size_t rewind;
|
||||
size_t max_rewind;
|
||||
size_t np = 0;
|
||||
|
||||
if (offset >= remaining)
|
||||
break;
|
||||
|
||||
at = (uint8_t *)memchr(data + start + offset, '@', remaining - offset);
|
||||
if (!at)
|
||||
break;
|
||||
|
||||
max_rewind = at - (data + start + offset);
|
||||
|
||||
found_at:
|
||||
for (rewind = 0; rewind < max_rewind; ++rewind) {
|
||||
uint8_t c = data[start + offset + max_rewind - rewind - 1];
|
||||
|
||||
if (cmark_isalnum(c))
|
||||
continue;
|
||||
|
||||
if (strchr(".+-_", c) != NULL)
|
||||
continue;
|
||||
|
||||
if (strchr(":", c) != NULL) {
|
||||
if (validate_protocol("mailto:", data + start + offset + max_rewind, rewind, max_rewind)) {
|
||||
auto_mailto = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (validate_protocol("xmpp:", data + start + offset + max_rewind, rewind, max_rewind)) {
|
||||
auto_mailto = false;
|
||||
is_xmpp = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (rewind == 0) {
|
||||
offset += max_rewind + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(data[start + offset + max_rewind] == '@');
|
||||
for (link_end = 1; link_end < remaining - offset - max_rewind; ++link_end) {
|
||||
uint8_t c = data[start + offset + max_rewind + link_end];
|
||||
|
||||
if (cmark_isalnum(c))
|
||||
continue;
|
||||
|
||||
if (c == '@') {
|
||||
// Found another '@', so go back and try again with an updated offset and max_rewind.
|
||||
offset += max_rewind + 1;
|
||||
max_rewind = link_end - 1;
|
||||
goto found_at;
|
||||
} else if (c == '.' && link_end < remaining - offset - max_rewind - 1 &&
|
||||
cmark_isalnum(data[start + offset + max_rewind + link_end + 1]))
|
||||
np++;
|
||||
else if (c == '/' && is_xmpp)
|
||||
continue;
|
||||
else if (c != '-' && c != '_')
|
||||
break;
|
||||
}
|
||||
|
||||
if (link_end < 2 || np == 0 ||
|
||||
(!cmark_isalpha(data[start + offset + max_rewind + link_end - 1]) &&
|
||||
data[start + offset + max_rewind + link_end - 1] != '.')) {
|
||||
offset += max_rewind + link_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
link_end = autolink_delim(data + start + offset + max_rewind, link_end);
|
||||
|
||||
if (link_end == 0) {
|
||||
offset += max_rewind + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
cmark_node *link_node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem);
|
||||
cmark_strbuf buf;
|
||||
cmark_strbuf_init(parser->mem, &buf, 10);
|
||||
if (auto_mailto)
|
||||
cmark_strbuf_puts(&buf, "mailto:");
|
||||
cmark_strbuf_put(&buf, data + start + offset + max_rewind - rewind, (bufsize_t)(link_end + rewind));
|
||||
link_node->as.link.url = cmark_chunk_buf_detach(&buf);
|
||||
|
||||
cmark_node *link_text = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
|
||||
cmark_chunk email = cmark_chunk_dup(
|
||||
&detached_chunk,
|
||||
(bufsize_t)(start + offset + max_rewind - rewind),
|
||||
(bufsize_t)(link_end + rewind));
|
||||
cmark_chunk_to_cstr(parser->mem, &email);
|
||||
link_text->as.literal = email;
|
||||
cmark_node_append_child(link_node, link_text);
|
||||
|
||||
cmark_node_insert_after(text, link_node);
|
||||
|
||||
cmark_node *post = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
|
||||
post->as.literal = cmark_chunk_dup(&detached_chunk,
|
||||
(bufsize_t)(start + offset + max_rewind + link_end),
|
||||
(bufsize_t)(remaining - offset - max_rewind - link_end));
|
||||
|
||||
cmark_node_insert_after(link_node, post);
|
||||
|
||||
text->as.literal = cmark_chunk_dup(&detached_chunk, (bufsize_t)start, (bufsize_t)(offset + max_rewind - rewind));
|
||||
cmark_chunk_to_cstr(parser->mem, &text->as.literal);
|
||||
|
||||
text = post;
|
||||
start += offset + max_rewind + link_end;
|
||||
remaining -= offset + max_rewind + link_end;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
// Convert the reference to allocated memory.
|
||||
assert(!text->as.literal.alloc);
|
||||
cmark_chunk_to_cstr(parser->mem, &text->as.literal);
|
||||
|
||||
// Free the detached buffer.
|
||||
cmark_chunk_free(parser->mem, &detached_chunk);
|
||||
}
|
||||
|
||||
static cmark_node *postprocess(cmark_syntax_extension *ext, cmark_parser *parser, cmark_node *root) {
|
||||
cmark_iter *iter;
|
||||
cmark_event_type ev;
|
||||
cmark_node *node;
|
||||
bool in_link = false;
|
||||
|
||||
cmark_consolidate_text_nodes(root);
|
||||
iter = cmark_iter_new(root);
|
||||
|
||||
while ((ev = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
node = cmark_iter_get_node(iter);
|
||||
if (in_link) {
|
||||
if (ev == CMARK_EVENT_EXIT && node->type == CMARK_NODE_LINK) {
|
||||
in_link = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev == CMARK_EVENT_ENTER && node->type == CMARK_NODE_LINK) {
|
||||
in_link = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ev == CMARK_EVENT_ENTER && node->type == CMARK_NODE_TEXT) {
|
||||
postprocess_text(parser, node);
|
||||
}
|
||||
}
|
||||
|
||||
cmark_iter_free(iter);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
cmark_syntax_extension *create_autolink_extension(void) {
|
||||
cmark_syntax_extension *ext = cmark_syntax_extension_new("autolink");
|
||||
cmark_llist *special_chars = NULL;
|
||||
|
||||
cmark_syntax_extension_set_match_inline_func(ext, match);
|
||||
cmark_syntax_extension_set_postprocess_func(ext, postprocess);
|
||||
|
||||
cmark_mem *mem = cmark_get_default_mem_allocator();
|
||||
special_chars = cmark_llist_append(mem, special_chars, (void *)':');
|
||||
special_chars = cmark_llist_append(mem, special_chars, (void *)'w');
|
||||
cmark_syntax_extension_set_special_inline_chars(ext, special_chars);
|
||||
|
||||
return ext;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef CMARK_GFM_AUTOLINK_H
|
||||
#define CMARK_GFM_AUTOLINK_H
|
||||
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
|
||||
cmark_syntax_extension *create_autolink_extension(void);
|
||||
|
||||
#endif
|
||||
@@ -1,31 +0,0 @@
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
#include "autolink.h"
|
||||
#include "mutex.h"
|
||||
#include "node.h"
|
||||
#include "strikethrough.h"
|
||||
#include "table.h"
|
||||
#include "tagfilter.h"
|
||||
#include "tasklist.h"
|
||||
#include "registry.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static int core_extensions_registration(cmark_plugin *plugin) {
|
||||
cmark_plugin_register_syntax_extension(plugin, create_table_extension());
|
||||
cmark_plugin_register_syntax_extension(plugin,
|
||||
create_strikethrough_extension());
|
||||
cmark_plugin_register_syntax_extension(plugin, create_autolink_extension());
|
||||
cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension());
|
||||
cmark_plugin_register_syntax_extension(plugin, create_tasklist_extension());
|
||||
return 1;
|
||||
}
|
||||
|
||||
CMARK_DEFINE_ONCE(registered);
|
||||
|
||||
static void register_plugins(void) {
|
||||
cmark_register_plugin(core_extensions_registration);
|
||||
}
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_gfm_core_extensions_ensure_registered(void) {
|
||||
CMARK_RUN_ONCE(registered, register_plugins);
|
||||
}
|
||||
@@ -1,879 +0,0 @@
|
||||
/* Generated by re2c 1.3 */
|
||||
|
||||
#include "ext_scanners.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
bufsize_t _ext_scan_at(bufsize_t (*scanner)(const unsigned char *),
|
||||
unsigned char *ptr, int len, bufsize_t offset) {
|
||||
bufsize_t res;
|
||||
|
||||
if (ptr == NULL || offset >= len) {
|
||||
return 0;
|
||||
} else {
|
||||
unsigned char lim = ptr[len];
|
||||
|
||||
ptr[len] = '\0';
|
||||
res = scanner(ptr + offset);
|
||||
ptr[len] = lim;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bufsize_t _scan_table_start(const unsigned char *p) {
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
|
||||
{
|
||||
unsigned char yych;
|
||||
static const unsigned char yybm[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
yych = *p;
|
||||
if (yych <= ' ') {
|
||||
if (yych <= '\n') {
|
||||
if (yych == '\t')
|
||||
goto yy4;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy4;
|
||||
if (yych >= ' ')
|
||||
goto yy4;
|
||||
}
|
||||
} else {
|
||||
if (yych <= '9') {
|
||||
if (yych == '-')
|
||||
goto yy5;
|
||||
} else {
|
||||
if (yych <= ':')
|
||||
goto yy6;
|
||||
if (yych == '|')
|
||||
goto yy4;
|
||||
}
|
||||
}
|
||||
++p;
|
||||
yy3 : { return 0; }
|
||||
yy4:
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy7;
|
||||
}
|
||||
if (yych == '-')
|
||||
goto yy10;
|
||||
if (yych == ':')
|
||||
goto yy12;
|
||||
goto yy3;
|
||||
yy5:
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy10;
|
||||
}
|
||||
if (yych <= ' ') {
|
||||
if (yych <= 0x08)
|
||||
goto yy3;
|
||||
if (yych <= '\r')
|
||||
goto yy14;
|
||||
if (yych <= 0x1F)
|
||||
goto yy3;
|
||||
goto yy14;
|
||||
} else {
|
||||
if (yych <= ':') {
|
||||
if (yych <= '9')
|
||||
goto yy3;
|
||||
goto yy13;
|
||||
} else {
|
||||
if (yych == '|')
|
||||
goto yy14;
|
||||
goto yy3;
|
||||
}
|
||||
}
|
||||
yy6:
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy10;
|
||||
}
|
||||
goto yy3;
|
||||
yy7:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy7;
|
||||
}
|
||||
if (yych == '-')
|
||||
goto yy10;
|
||||
if (yych == ':')
|
||||
goto yy12;
|
||||
yy9:
|
||||
p = marker;
|
||||
goto yy3;
|
||||
yy10:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy10;
|
||||
}
|
||||
if (yych <= 0x1F) {
|
||||
if (yych <= '\n') {
|
||||
if (yych <= 0x08)
|
||||
goto yy9;
|
||||
if (yych <= '\t')
|
||||
goto yy13;
|
||||
goto yy15;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy13;
|
||||
if (yych <= '\r')
|
||||
goto yy17;
|
||||
goto yy9;
|
||||
}
|
||||
} else {
|
||||
if (yych <= ':') {
|
||||
if (yych <= ' ')
|
||||
goto yy13;
|
||||
if (yych <= '9')
|
||||
goto yy9;
|
||||
goto yy13;
|
||||
} else {
|
||||
if (yych == '|')
|
||||
goto yy18;
|
||||
goto yy9;
|
||||
}
|
||||
}
|
||||
yy12:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy10;
|
||||
}
|
||||
goto yy9;
|
||||
yy13:
|
||||
yych = *++p;
|
||||
yy14:
|
||||
if (yych <= '\r') {
|
||||
if (yych <= '\t') {
|
||||
if (yych <= 0x08)
|
||||
goto yy9;
|
||||
goto yy13;
|
||||
} else {
|
||||
if (yych <= '\n')
|
||||
goto yy15;
|
||||
if (yych <= '\f')
|
||||
goto yy13;
|
||||
goto yy17;
|
||||
}
|
||||
} else {
|
||||
if (yych <= ' ') {
|
||||
if (yych <= 0x1F)
|
||||
goto yy9;
|
||||
goto yy13;
|
||||
} else {
|
||||
if (yych == '|')
|
||||
goto yy18;
|
||||
goto yy9;
|
||||
}
|
||||
}
|
||||
yy15:
|
||||
++p;
|
||||
{ return (bufsize_t)(p - start); }
|
||||
yy17:
|
||||
yych = *++p;
|
||||
if (yych == '\n')
|
||||
goto yy15;
|
||||
goto yy9;
|
||||
yy18:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy10;
|
||||
}
|
||||
if (yych <= '\r') {
|
||||
if (yych <= '\t') {
|
||||
if (yych <= 0x08)
|
||||
goto yy9;
|
||||
goto yy18;
|
||||
} else {
|
||||
if (yych <= '\n')
|
||||
goto yy15;
|
||||
if (yych <= '\f')
|
||||
goto yy18;
|
||||
goto yy17;
|
||||
}
|
||||
} else {
|
||||
if (yych <= ' ') {
|
||||
if (yych <= 0x1F)
|
||||
goto yy9;
|
||||
goto yy18;
|
||||
} else {
|
||||
if (yych == ':')
|
||||
goto yy12;
|
||||
goto yy9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bufsize_t _scan_table_cell(const unsigned char *p) {
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
|
||||
{
|
||||
unsigned char yych;
|
||||
unsigned int yyaccept = 0;
|
||||
static const unsigned char yybm[] = {
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 64, 0, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64,
|
||||
64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
};
|
||||
yych = *p;
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy22;
|
||||
}
|
||||
if (yych <= 0xEC) {
|
||||
if (yych <= 0xC1) {
|
||||
if (yych <= '\r')
|
||||
goto yy25;
|
||||
if (yych <= '\\')
|
||||
goto yy27;
|
||||
goto yy25;
|
||||
} else {
|
||||
if (yych <= 0xDF)
|
||||
goto yy29;
|
||||
if (yych <= 0xE0)
|
||||
goto yy30;
|
||||
goto yy31;
|
||||
}
|
||||
} else {
|
||||
if (yych <= 0xF0) {
|
||||
if (yych <= 0xED)
|
||||
goto yy32;
|
||||
if (yych <= 0xEF)
|
||||
goto yy31;
|
||||
goto yy33;
|
||||
} else {
|
||||
if (yych <= 0xF3)
|
||||
goto yy34;
|
||||
if (yych <= 0xF4)
|
||||
goto yy35;
|
||||
goto yy25;
|
||||
}
|
||||
}
|
||||
yy22:
|
||||
yyaccept = 0;
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy22;
|
||||
}
|
||||
if (yych <= 0xEC) {
|
||||
if (yych <= 0xC1) {
|
||||
if (yych <= '\r')
|
||||
goto yy24;
|
||||
if (yych <= '\\')
|
||||
goto yy27;
|
||||
} else {
|
||||
if (yych <= 0xDF)
|
||||
goto yy36;
|
||||
if (yych <= 0xE0)
|
||||
goto yy38;
|
||||
goto yy39;
|
||||
}
|
||||
} else {
|
||||
if (yych <= 0xF0) {
|
||||
if (yych <= 0xED)
|
||||
goto yy40;
|
||||
if (yych <= 0xEF)
|
||||
goto yy39;
|
||||
goto yy41;
|
||||
} else {
|
||||
if (yych <= 0xF3)
|
||||
goto yy42;
|
||||
if (yych <= 0xF4)
|
||||
goto yy43;
|
||||
}
|
||||
}
|
||||
yy24 : { return (bufsize_t)(p - start); }
|
||||
yy25:
|
||||
++p;
|
||||
yy26 : { return 0; }
|
||||
yy27:
|
||||
yyaccept = 0;
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy27;
|
||||
}
|
||||
if (yych <= 0xDF) {
|
||||
if (yych <= '\f') {
|
||||
if (yych == '\n')
|
||||
goto yy24;
|
||||
goto yy22;
|
||||
} else {
|
||||
if (yych <= '\r')
|
||||
goto yy24;
|
||||
if (yych <= 0x7F)
|
||||
goto yy22;
|
||||
if (yych <= 0xC1)
|
||||
goto yy24;
|
||||
goto yy36;
|
||||
}
|
||||
} else {
|
||||
if (yych <= 0xEF) {
|
||||
if (yych <= 0xE0)
|
||||
goto yy38;
|
||||
if (yych == 0xED)
|
||||
goto yy40;
|
||||
goto yy39;
|
||||
} else {
|
||||
if (yych <= 0xF0)
|
||||
goto yy41;
|
||||
if (yych <= 0xF3)
|
||||
goto yy42;
|
||||
if (yych <= 0xF4)
|
||||
goto yy43;
|
||||
goto yy24;
|
||||
}
|
||||
}
|
||||
yy29:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy26;
|
||||
if (yych <= 0xBF)
|
||||
goto yy22;
|
||||
goto yy26;
|
||||
yy30:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x9F)
|
||||
goto yy26;
|
||||
if (yych <= 0xBF)
|
||||
goto yy36;
|
||||
goto yy26;
|
||||
yy31:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x7F)
|
||||
goto yy26;
|
||||
if (yych <= 0xBF)
|
||||
goto yy36;
|
||||
goto yy26;
|
||||
yy32:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x7F)
|
||||
goto yy26;
|
||||
if (yych <= 0x9F)
|
||||
goto yy36;
|
||||
goto yy26;
|
||||
yy33:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x8F)
|
||||
goto yy26;
|
||||
if (yych <= 0xBF)
|
||||
goto yy39;
|
||||
goto yy26;
|
||||
yy34:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x7F)
|
||||
goto yy26;
|
||||
if (yych <= 0xBF)
|
||||
goto yy39;
|
||||
goto yy26;
|
||||
yy35:
|
||||
yyaccept = 1;
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x7F)
|
||||
goto yy26;
|
||||
if (yych <= 0x8F)
|
||||
goto yy39;
|
||||
goto yy26;
|
||||
yy36:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy37;
|
||||
if (yych <= 0xBF)
|
||||
goto yy22;
|
||||
yy37:
|
||||
p = marker;
|
||||
if (yyaccept == 0) {
|
||||
goto yy24;
|
||||
} else {
|
||||
goto yy26;
|
||||
}
|
||||
yy38:
|
||||
yych = *++p;
|
||||
if (yych <= 0x9F)
|
||||
goto yy37;
|
||||
if (yych <= 0xBF)
|
||||
goto yy36;
|
||||
goto yy37;
|
||||
yy39:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy37;
|
||||
if (yych <= 0xBF)
|
||||
goto yy36;
|
||||
goto yy37;
|
||||
yy40:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy37;
|
||||
if (yych <= 0x9F)
|
||||
goto yy36;
|
||||
goto yy37;
|
||||
yy41:
|
||||
yych = *++p;
|
||||
if (yych <= 0x8F)
|
||||
goto yy37;
|
||||
if (yych <= 0xBF)
|
||||
goto yy39;
|
||||
goto yy37;
|
||||
yy42:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy37;
|
||||
if (yych <= 0xBF)
|
||||
goto yy39;
|
||||
goto yy37;
|
||||
yy43:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy37;
|
||||
if (yych <= 0x8F)
|
||||
goto yy39;
|
||||
goto yy37;
|
||||
}
|
||||
}
|
||||
|
||||
bufsize_t _scan_table_cell_end(const unsigned char *p) {
|
||||
const unsigned char *start = p;
|
||||
|
||||
{
|
||||
unsigned char yych;
|
||||
static const unsigned char yybm[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
yych = *p;
|
||||
if (yych == '|')
|
||||
goto yy48;
|
||||
++p;
|
||||
{ return 0; }
|
||||
yy48:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy48;
|
||||
}
|
||||
{ return (bufsize_t)(p - start); }
|
||||
}
|
||||
}
|
||||
|
||||
bufsize_t _scan_table_row_end(const unsigned char *p) {
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
|
||||
{
|
||||
unsigned char yych;
|
||||
static const unsigned char yybm[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
yych = *p;
|
||||
if (yych <= '\f') {
|
||||
if (yych <= 0x08)
|
||||
goto yy53;
|
||||
if (yych == '\n')
|
||||
goto yy56;
|
||||
goto yy55;
|
||||
} else {
|
||||
if (yych <= '\r')
|
||||
goto yy58;
|
||||
if (yych == ' ')
|
||||
goto yy55;
|
||||
}
|
||||
yy53:
|
||||
++p;
|
||||
yy54 : { return 0; }
|
||||
yy55:
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x08)
|
||||
goto yy54;
|
||||
if (yych <= '\r')
|
||||
goto yy60;
|
||||
if (yych == ' ')
|
||||
goto yy60;
|
||||
goto yy54;
|
||||
yy56:
|
||||
++p;
|
||||
{ return (bufsize_t)(p - start); }
|
||||
yy58:
|
||||
yych = *++p;
|
||||
if (yych == '\n')
|
||||
goto yy56;
|
||||
goto yy54;
|
||||
yy59:
|
||||
yych = *++p;
|
||||
yy60:
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy59;
|
||||
}
|
||||
if (yych <= 0x08)
|
||||
goto yy61;
|
||||
if (yych <= '\n')
|
||||
goto yy56;
|
||||
if (yych <= '\r')
|
||||
goto yy62;
|
||||
yy61:
|
||||
p = marker;
|
||||
goto yy54;
|
||||
yy62:
|
||||
yych = *++p;
|
||||
if (yych == '\n')
|
||||
goto yy56;
|
||||
goto yy61;
|
||||
}
|
||||
}
|
||||
|
||||
bufsize_t _scan_tasklist(const unsigned char *p) {
|
||||
const unsigned char *marker = NULL;
|
||||
const unsigned char *start = p;
|
||||
|
||||
{
|
||||
unsigned char yych;
|
||||
static const unsigned char yybm[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 64, 64, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
yych = *p;
|
||||
if (yych <= ' ') {
|
||||
if (yych <= '\n') {
|
||||
if (yych == '\t')
|
||||
goto yy67;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy67;
|
||||
if (yych >= ' ')
|
||||
goto yy67;
|
||||
}
|
||||
} else {
|
||||
if (yych <= ',') {
|
||||
if (yych <= ')')
|
||||
goto yy65;
|
||||
if (yych <= '+')
|
||||
goto yy68;
|
||||
} else {
|
||||
if (yych <= '-')
|
||||
goto yy68;
|
||||
if (yych <= '/')
|
||||
goto yy65;
|
||||
if (yych <= '9')
|
||||
goto yy69;
|
||||
}
|
||||
}
|
||||
yy65:
|
||||
++p;
|
||||
yy66 : { return 0; }
|
||||
yy67:
|
||||
yych = *(marker = ++p);
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy70;
|
||||
}
|
||||
if (yych <= ',') {
|
||||
if (yych <= ')')
|
||||
goto yy66;
|
||||
if (yych <= '+')
|
||||
goto yy73;
|
||||
goto yy66;
|
||||
} else {
|
||||
if (yych <= '-')
|
||||
goto yy73;
|
||||
if (yych <= '/')
|
||||
goto yy66;
|
||||
if (yych <= '9')
|
||||
goto yy74;
|
||||
goto yy66;
|
||||
}
|
||||
yy68:
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= '\n') {
|
||||
if (yych == '\t')
|
||||
goto yy75;
|
||||
goto yy66;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy75;
|
||||
if (yych == ' ')
|
||||
goto yy75;
|
||||
goto yy66;
|
||||
}
|
||||
yy69:
|
||||
yych = *(marker = ++p);
|
||||
if (yych <= 0x1F) {
|
||||
if (yych <= '\t') {
|
||||
if (yych <= 0x08)
|
||||
goto yy78;
|
||||
goto yy73;
|
||||
} else {
|
||||
if (yych <= '\n')
|
||||
goto yy66;
|
||||
if (yych <= '\f')
|
||||
goto yy73;
|
||||
goto yy78;
|
||||
}
|
||||
} else {
|
||||
if (yych <= 0x7F) {
|
||||
if (yych <= ' ')
|
||||
goto yy73;
|
||||
goto yy78;
|
||||
} else {
|
||||
if (yych <= 0xC1)
|
||||
goto yy66;
|
||||
if (yych <= 0xF4)
|
||||
goto yy78;
|
||||
goto yy66;
|
||||
}
|
||||
}
|
||||
yy70:
|
||||
yych = *++p;
|
||||
if (yybm[0 + yych] & 64) {
|
||||
goto yy70;
|
||||
}
|
||||
if (yych <= ',') {
|
||||
if (yych <= ')')
|
||||
goto yy72;
|
||||
if (yych <= '+')
|
||||
goto yy73;
|
||||
} else {
|
||||
if (yych <= '-')
|
||||
goto yy73;
|
||||
if (yych <= '/')
|
||||
goto yy72;
|
||||
if (yych <= '9')
|
||||
goto yy74;
|
||||
}
|
||||
yy72:
|
||||
p = marker;
|
||||
goto yy66;
|
||||
yy73:
|
||||
yych = *++p;
|
||||
if (yych == '[')
|
||||
goto yy72;
|
||||
goto yy76;
|
||||
yy74:
|
||||
yych = *++p;
|
||||
if (yych <= '\n') {
|
||||
if (yych == '\t')
|
||||
goto yy73;
|
||||
goto yy78;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy73;
|
||||
if (yych == ' ')
|
||||
goto yy73;
|
||||
goto yy78;
|
||||
}
|
||||
yy75:
|
||||
yych = *++p;
|
||||
yy76:
|
||||
if (yych <= '\f') {
|
||||
if (yych == '\t')
|
||||
goto yy75;
|
||||
if (yych <= '\n')
|
||||
goto yy72;
|
||||
goto yy75;
|
||||
} else {
|
||||
if (yych <= ' ') {
|
||||
if (yych <= 0x1F)
|
||||
goto yy72;
|
||||
goto yy75;
|
||||
} else {
|
||||
if (yych == '[')
|
||||
goto yy86;
|
||||
goto yy72;
|
||||
}
|
||||
}
|
||||
yy77:
|
||||
yych = *++p;
|
||||
yy78:
|
||||
if (yybm[0 + yych] & 128) {
|
||||
goto yy77;
|
||||
}
|
||||
if (yych <= 0xC1) {
|
||||
if (yych <= '\f') {
|
||||
if (yych <= 0x08)
|
||||
goto yy73;
|
||||
if (yych == '\n')
|
||||
goto yy72;
|
||||
goto yy75;
|
||||
} else {
|
||||
if (yych == ' ')
|
||||
goto yy75;
|
||||
if (yych <= 0x7F)
|
||||
goto yy73;
|
||||
goto yy72;
|
||||
}
|
||||
} else {
|
||||
if (yych <= 0xED) {
|
||||
if (yych <= 0xDF)
|
||||
goto yy79;
|
||||
if (yych <= 0xE0)
|
||||
goto yy80;
|
||||
if (yych <= 0xEC)
|
||||
goto yy81;
|
||||
goto yy82;
|
||||
} else {
|
||||
if (yych <= 0xF0) {
|
||||
if (yych <= 0xEF)
|
||||
goto yy81;
|
||||
goto yy83;
|
||||
} else {
|
||||
if (yych <= 0xF3)
|
||||
goto yy84;
|
||||
if (yych <= 0xF4)
|
||||
goto yy85;
|
||||
goto yy72;
|
||||
}
|
||||
}
|
||||
}
|
||||
yy79:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy72;
|
||||
if (yych <= 0xBF)
|
||||
goto yy73;
|
||||
goto yy72;
|
||||
yy80:
|
||||
yych = *++p;
|
||||
if (yych <= 0x9F)
|
||||
goto yy72;
|
||||
if (yych <= 0xBF)
|
||||
goto yy79;
|
||||
goto yy72;
|
||||
yy81:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy72;
|
||||
if (yych <= 0xBF)
|
||||
goto yy79;
|
||||
goto yy72;
|
||||
yy82:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy72;
|
||||
if (yych <= 0x9F)
|
||||
goto yy79;
|
||||
goto yy72;
|
||||
yy83:
|
||||
yych = *++p;
|
||||
if (yych <= 0x8F)
|
||||
goto yy72;
|
||||
if (yych <= 0xBF)
|
||||
goto yy81;
|
||||
goto yy72;
|
||||
yy84:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy72;
|
||||
if (yych <= 0xBF)
|
||||
goto yy81;
|
||||
goto yy72;
|
||||
yy85:
|
||||
yych = *++p;
|
||||
if (yych <= 0x7F)
|
||||
goto yy72;
|
||||
if (yych <= 0x8F)
|
||||
goto yy81;
|
||||
goto yy72;
|
||||
yy86:
|
||||
yych = *++p;
|
||||
if (yych <= 'W') {
|
||||
if (yych != ' ')
|
||||
goto yy72;
|
||||
} else {
|
||||
if (yych <= 'X')
|
||||
goto yy87;
|
||||
if (yych != 'x')
|
||||
goto yy72;
|
||||
}
|
||||
yy87:
|
||||
yych = *++p;
|
||||
if (yych != ']')
|
||||
goto yy72;
|
||||
yych = *++p;
|
||||
if (yych <= '\n') {
|
||||
if (yych != '\t')
|
||||
goto yy72;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy89;
|
||||
if (yych != ' ')
|
||||
goto yy72;
|
||||
}
|
||||
yy89:
|
||||
yych = *++p;
|
||||
if (yych <= '\n') {
|
||||
if (yych == '\t')
|
||||
goto yy89;
|
||||
} else {
|
||||
if (yych <= '\f')
|
||||
goto yy89;
|
||||
if (yych == ' ')
|
||||
goto yy89;
|
||||
}
|
||||
{ return (bufsize_t)(p - start); }
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "chunk.h"
|
||||
#include "cmark-gfm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bufsize_t _ext_scan_at(bufsize_t (*scanner)(const unsigned char *),
|
||||
unsigned char *ptr, int len, bufsize_t offset);
|
||||
bufsize_t _scan_table_start(const unsigned char *p);
|
||||
bufsize_t _scan_table_cell(const unsigned char *p);
|
||||
bufsize_t _scan_table_cell_end(const unsigned char *p);
|
||||
bufsize_t _scan_table_row_end(const unsigned char *p);
|
||||
bufsize_t _scan_tasklist(const unsigned char *p);
|
||||
|
||||
#define scan_table_start(c, l, n) _ext_scan_at(&_scan_table_start, c, l, n)
|
||||
#define scan_table_cell(c, l, n) _ext_scan_at(&_scan_table_cell, c, l, n)
|
||||
#define scan_table_cell_end(c, l, n) _ext_scan_at(&_scan_table_cell_end, c, l, n)
|
||||
#define scan_table_row_end(c, l, n) _ext_scan_at(&_scan_table_row_end, c, l, n)
|
||||
#define scan_tasklist(c, l, n) _ext_scan_at(&_scan_tasklist, c, l, n)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
#ifndef CMARK_GFM_CORE_EXTENSIONS_H
|
||||
#define CMARK_GFM_CORE_EXTENSIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
#include "export.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_gfm_core_extensions_ensure_registered(void);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node);
|
||||
|
||||
/** Sets the number of columns for the table, returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node);
|
||||
|
||||
/** Sets the alignments for the table, returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node);
|
||||
|
||||
/** Sets the column span for the table cell, returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_table_cell_colspan(cmark_node *node, unsigned colspan);
|
||||
|
||||
/** Sets the row span for the table cell, returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_table_cell_rowspan(cmark_node *node, unsigned rowspan);
|
||||
|
||||
/**
|
||||
Gets the column span for the table cell, returning \c UINT_MAX on error.
|
||||
|
||||
A value of 0 indicates that the cell is a "filler" cell, intended to be overlapped with a previous
|
||||
cell with a span > 1.
|
||||
|
||||
Column span is only parsed when \c CMARK_OPT_TABLE_SPANS is set.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
unsigned cmark_gfm_extensions_get_table_cell_colspan(cmark_node *node);
|
||||
|
||||
/**
|
||||
Gets the row span for the table cell, returning \c UINT_MAX on error.
|
||||
|
||||
A value of 0 indicates that the cell is a "filler" cell, intended to be overlapped with a previous
|
||||
cell with a span > 1.
|
||||
|
||||
Row span is only parsed when \c CMARK_OPT_TABLE_SPANS is set.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
unsigned cmark_gfm_extensions_get_table_cell_rowspan(cmark_node *node);
|
||||
|
||||
/** Sets whether the node is a table header row, returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bool cmark_gfm_extensions_get_tasklist_item_checked(cmark_node *node);
|
||||
/* For backwards compatibility */
|
||||
#define cmark_gfm_extensions_tasklist_is_checked cmark_gfm_extensions_get_tasklist_item_checked
|
||||
|
||||
/** Sets whether a tasklist item is "checked" (completed), returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
module cmark_gfm_extensions {
|
||||
header "cmark-gfm-core-extensions.h"
|
||||
}
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "strikethrough.h"
|
||||
#include <parser.h>
|
||||
#include <render.h>
|
||||
|
||||
cmark_node_type CMARK_NODE_STRIKETHROUGH;
|
||||
|
||||
static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser,
|
||||
cmark_node *parent, unsigned char character,
|
||||
cmark_inline_parser *inline_parser) {
|
||||
cmark_node *res = NULL;
|
||||
int left_flanking, right_flanking, punct_before, punct_after, delims;
|
||||
char buffer[101];
|
||||
|
||||
if (character != '~')
|
||||
return NULL;
|
||||
|
||||
delims = cmark_inline_parser_scan_delimiters(
|
||||
inline_parser, sizeof(buffer) - 1, '~',
|
||||
&left_flanking,
|
||||
&right_flanking, &punct_before, &punct_after);
|
||||
|
||||
memset(buffer, '~', delims);
|
||||
buffer[delims] = 0;
|
||||
|
||||
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
|
||||
cmark_node_set_literal(res, buffer);
|
||||
res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
|
||||
res->start_column = cmark_inline_parser_get_column(inline_parser) - delims;
|
||||
|
||||
if ((left_flanking || right_flanking) &&
|
||||
(delims == 2 || (!(parser->options & CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE) && delims == 1))) {
|
||||
cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
|
||||
right_flanking, res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
|
||||
cmark_inline_parser *inline_parser, delimiter *opener,
|
||||
delimiter *closer) {
|
||||
cmark_node *strikethrough;
|
||||
cmark_node *tmp, *next;
|
||||
delimiter *delim, *tmp_delim;
|
||||
delimiter *res = closer->next;
|
||||
|
||||
strikethrough = opener->inl_text;
|
||||
|
||||
if (opener->inl_text->as.literal.len != closer->inl_text->as.literal.len)
|
||||
goto done;
|
||||
|
||||
if (!cmark_node_set_type(strikethrough, CMARK_NODE_STRIKETHROUGH))
|
||||
goto done;
|
||||
|
||||
cmark_node_set_syntax_extension(strikethrough, self);
|
||||
|
||||
tmp = cmark_node_next(opener->inl_text);
|
||||
|
||||
while (tmp) {
|
||||
if (tmp == closer->inl_text)
|
||||
break;
|
||||
next = cmark_node_next(tmp);
|
||||
cmark_node_append_child(strikethrough, tmp);
|
||||
tmp = next;
|
||||
}
|
||||
|
||||
strikethrough->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
|
||||
cmark_node_free(closer->inl_text);
|
||||
|
||||
done:
|
||||
delim = closer;
|
||||
while (delim != NULL && delim != opener) {
|
||||
tmp_delim = delim->previous;
|
||||
cmark_inline_parser_remove_delimiter(inline_parser, delim);
|
||||
delim = tmp_delim;
|
||||
}
|
||||
|
||||
cmark_inline_parser_remove_delimiter(inline_parser, opener);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *get_type_string(cmark_syntax_extension *extension,
|
||||
cmark_node *node) {
|
||||
return node->type == CMARK_NODE_STRIKETHROUGH ? "strikethrough" : "<unknown>";
|
||||
}
|
||||
|
||||
static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
|
||||
cmark_node_type child_type) {
|
||||
if (node->type != CMARK_NODE_STRIKETHROUGH)
|
||||
return false;
|
||||
|
||||
return CMARK_NODE_TYPE_INLINE_P(child_type);
|
||||
}
|
||||
|
||||
static void commonmark_render(cmark_syntax_extension *extension,
|
||||
cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
renderer->out(renderer, node, "~~", false, LITERAL);
|
||||
}
|
||||
|
||||
static void latex_render(cmark_syntax_extension *extension,
|
||||
cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
// requires \usepackage{ulem}
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
if (entering) {
|
||||
renderer->out(renderer, node, "\\sout{", false, LITERAL);
|
||||
} else {
|
||||
renderer->out(renderer, node, "}", false, LITERAL);
|
||||
}
|
||||
}
|
||||
|
||||
static void man_render(cmark_syntax_extension *extension,
|
||||
cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
if (entering) {
|
||||
renderer->cr(renderer);
|
||||
renderer->out(renderer, node, ".ST \"", false, LITERAL);
|
||||
} else {
|
||||
renderer->out(renderer, node, "\"", false, LITERAL);
|
||||
renderer->cr(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
static void html_render(cmark_syntax_extension *extension,
|
||||
cmark_html_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(renderer->html, "<del>");
|
||||
} else {
|
||||
cmark_strbuf_puts(renderer->html, "</del>");
|
||||
}
|
||||
}
|
||||
|
||||
static void plaintext_render(cmark_syntax_extension *extension,
|
||||
cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
renderer->out(renderer, node, "~", false, LITERAL);
|
||||
}
|
||||
|
||||
cmark_syntax_extension *create_strikethrough_extension(void) {
|
||||
cmark_syntax_extension *ext = cmark_syntax_extension_new("strikethrough");
|
||||
cmark_llist *special_chars = NULL;
|
||||
|
||||
cmark_syntax_extension_set_get_type_string_func(ext, get_type_string);
|
||||
cmark_syntax_extension_set_can_contain_func(ext, can_contain);
|
||||
cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render);
|
||||
cmark_syntax_extension_set_latex_render_func(ext, latex_render);
|
||||
cmark_syntax_extension_set_man_render_func(ext, man_render);
|
||||
cmark_syntax_extension_set_html_render_func(ext, html_render);
|
||||
cmark_syntax_extension_set_plaintext_render_func(ext, plaintext_render);
|
||||
CMARK_NODE_STRIKETHROUGH = cmark_syntax_extension_add_node(1);
|
||||
|
||||
cmark_syntax_extension_set_match_inline_func(ext, match);
|
||||
cmark_syntax_extension_set_inline_from_delim_func(ext, insert);
|
||||
|
||||
cmark_mem *mem = cmark_get_default_mem_allocator();
|
||||
special_chars = cmark_llist_append(mem, special_chars, (void *)'~');
|
||||
cmark_syntax_extension_set_special_inline_chars(ext, special_chars);
|
||||
|
||||
cmark_syntax_extension_set_emphasis(ext, 1);
|
||||
|
||||
return ext;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef CMARK_GFM_STRIKETHROUGH_H
|
||||
#define CMARK_GFM_STRIKETHROUGH_H
|
||||
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
|
||||
extern cmark_node_type CMARK_NODE_STRIKETHROUGH;
|
||||
cmark_syntax_extension *create_strikethrough_extension(void);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
||||
#ifndef CMARK_GFM_TABLE_H
|
||||
#define CMARK_GFM_TABLE_H
|
||||
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
|
||||
|
||||
extern cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW,
|
||||
CMARK_NODE_TABLE_CELL;
|
||||
|
||||
cmark_syntax_extension *create_table_extension(void);
|
||||
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "tagfilter.h"
|
||||
#include <parser.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static const char *blacklist[] = {
|
||||
"title", "textarea", "style", "xmp", "iframe",
|
||||
"noembed", "noframes", "script", "plaintext", NULL,
|
||||
};
|
||||
|
||||
static int is_tag(const unsigned char *tag_data, size_t tag_size,
|
||||
const char *tagname) {
|
||||
size_t i;
|
||||
|
||||
if (tag_size < 3 || tag_data[0] != '<')
|
||||
return 0;
|
||||
|
||||
i = 1;
|
||||
|
||||
if (tag_data[i] == '/') {
|
||||
i++;
|
||||
}
|
||||
|
||||
for (; i < tag_size; ++i, ++tagname) {
|
||||
if (*tagname == 0)
|
||||
break;
|
||||
|
||||
if (tolower(tag_data[i]) != *tagname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == tag_size)
|
||||
return 0;
|
||||
|
||||
if (cmark_isspace(tag_data[i]) || tag_data[i] == '>')
|
||||
return 1;
|
||||
|
||||
if (tag_data[i] == '/' && tag_size >= i + 2 && tag_data[i + 1] == '>')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filter(cmark_syntax_extension *ext, const unsigned char *tag,
|
||||
size_t tag_len) {
|
||||
const char **it;
|
||||
|
||||
for (it = blacklist; *it; ++it) {
|
||||
if (is_tag(tag, tag_len, *it)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
cmark_syntax_extension *create_tagfilter_extension(void) {
|
||||
cmark_syntax_extension *ext = cmark_syntax_extension_new("tagfilter");
|
||||
cmark_syntax_extension_set_html_filter_func(ext, filter);
|
||||
return ext;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef CMARK_GFM_TAGFILTER_H
|
||||
#define CMARK_GFM_TAGFILTER_H
|
||||
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
|
||||
cmark_syntax_extension *create_tagfilter_extension(void);
|
||||
|
||||
#endif
|
||||
@@ -1,160 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "tasklist.h"
|
||||
#include <parser.h>
|
||||
#include <render.h>
|
||||
#include <html.h>
|
||||
#include "ext_scanners.h"
|
||||
|
||||
typedef enum {
|
||||
CMARK_TASKLIST_NOCHECKED,
|
||||
CMARK_TASKLIST_CHECKED,
|
||||
} cmark_tasklist_type;
|
||||
|
||||
// Local constants
|
||||
static const char *TYPE_STRING = "tasklist";
|
||||
|
||||
static const char *get_type_string(cmark_syntax_extension *extension, cmark_node *node) {
|
||||
return TYPE_STRING;
|
||||
}
|
||||
|
||||
|
||||
// Return 1 if state was set, 0 otherwise
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked) {
|
||||
// The node has to exist, and be an extension, and actually be the right type in order to get the value.
|
||||
if (!node || !node->extension || strcmp(cmark_node_get_type_string(node), TYPE_STRING))
|
||||
return 0;
|
||||
|
||||
node->as.list.checked = is_checked;
|
||||
return 1;
|
||||
}
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bool cmark_gfm_extensions_get_tasklist_item_checked(cmark_node *node) {
|
||||
if (!node || !node->extension || strcmp(cmark_node_get_type_string(node), TYPE_STRING))
|
||||
return false;
|
||||
|
||||
if (node->as.list.checked) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_node_item_prefix(cmark_parser *parser, const char *input,
|
||||
cmark_node *container) {
|
||||
bool res = false;
|
||||
|
||||
if (parser->indent >=
|
||||
container->as.list.marker_offset + container->as.list.padding) {
|
||||
cmark_parser_advance_offset(parser, input, container->as.list.marker_offset +
|
||||
container->as.list.padding,
|
||||
true);
|
||||
res = true;
|
||||
} else if (parser->blank && container->first_child != NULL) {
|
||||
// if container->first_child is NULL, then the opening line
|
||||
// of the list item was blank after the list marker; in this
|
||||
// case, we are done with the list item.
|
||||
cmark_parser_advance_offset(parser, input, parser->first_nonspace - parser->offset,
|
||||
false);
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int matches(cmark_syntax_extension *self, cmark_parser *parser,
|
||||
unsigned char *input, int len,
|
||||
cmark_node *parent_container) {
|
||||
return parse_node_item_prefix(parser, (const char*)input, parent_container);
|
||||
}
|
||||
|
||||
static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
|
||||
cmark_node_type child_type) {
|
||||
return (node->type == CMARK_NODE_ITEM) ? 1 : 0;
|
||||
}
|
||||
|
||||
static cmark_node *open_tasklist_item(cmark_syntax_extension *self,
|
||||
int indented, cmark_parser *parser,
|
||||
cmark_node *parent_container,
|
||||
unsigned char *input, int len) {
|
||||
cmark_node_type node_type = cmark_node_get_type(parent_container);
|
||||
if (node_type != CMARK_NODE_ITEM) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bufsize_t matched = scan_tasklist(input, len, 0);
|
||||
if (!matched) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmark_node_set_syntax_extension(parent_container, self);
|
||||
cmark_parser_advance_offset(parser, (char *)input, 3, false);
|
||||
|
||||
// Either an upper or lower case X means the task is completed.
|
||||
parent_container->as.list.checked = (strstr((char*)input, "[x]") || strstr((char*)input, "[X]"));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void commonmark_render(cmark_syntax_extension *extension,
|
||||
cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
if (entering) {
|
||||
renderer->cr(renderer);
|
||||
if (node->as.list.checked) {
|
||||
renderer->out(renderer, node, "- [x] ", false, LITERAL);
|
||||
} else {
|
||||
renderer->out(renderer, node, "- [ ] ", false, LITERAL);
|
||||
}
|
||||
cmark_strbuf_puts(renderer->prefix, " ");
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
|
||||
renderer->cr(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
static void html_render(cmark_syntax_extension *extension,
|
||||
cmark_html_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
if (entering) {
|
||||
cmark_html_render_cr(renderer->html);
|
||||
cmark_strbuf_puts(renderer->html, "<li");
|
||||
cmark_html_render_sourcepos(node, renderer->html, options);
|
||||
cmark_strbuf_putc(renderer->html, '>');
|
||||
if (node->as.list.checked) {
|
||||
cmark_strbuf_puts(renderer->html, "<input type=\"checkbox\" checked=\"\" disabled=\"\" /> ");
|
||||
} else {
|
||||
cmark_strbuf_puts(renderer->html, "<input type=\"checkbox\" disabled=\"\" /> ");
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_puts(renderer->html, "</li>\n");
|
||||
}
|
||||
}
|
||||
|
||||
static const char *xml_attr(cmark_syntax_extension *extension,
|
||||
cmark_node *node) {
|
||||
if (node->as.list.checked) {
|
||||
return " completed=\"true\"";
|
||||
} else {
|
||||
return " completed=\"false\"";
|
||||
}
|
||||
}
|
||||
|
||||
cmark_syntax_extension *create_tasklist_extension(void) {
|
||||
cmark_syntax_extension *ext = cmark_syntax_extension_new("tasklist");
|
||||
|
||||
cmark_syntax_extension_set_match_block_func(ext, matches);
|
||||
cmark_syntax_extension_set_get_type_string_func(ext, get_type_string);
|
||||
cmark_syntax_extension_set_open_block_func(ext, open_tasklist_item);
|
||||
cmark_syntax_extension_set_can_contain_func(ext, can_contain);
|
||||
cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render);
|
||||
cmark_syntax_extension_set_plaintext_render_func(ext, commonmark_render);
|
||||
cmark_syntax_extension_set_html_render_func(ext, html_render);
|
||||
cmark_syntax_extension_set_xml_attr_func(ext, xml_attr);
|
||||
|
||||
return ext;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef TASKLIST_H
|
||||
#define TASKLIST_H
|
||||
|
||||
#include "cmark-gfm-core-extensions.h"
|
||||
|
||||
cmark_syntax_extension *create_tasklist_extension(void);
|
||||
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "cmark-gfm.h"
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
#include "mutex.h"
|
||||
|
||||
CMARK_DEFINE_LOCK(arena)
|
||||
|
||||
static struct arena_chunk {
|
||||
size_t sz, used;
|
||||
uint8_t push_point;
|
||||
void *ptr;
|
||||
struct arena_chunk *prev;
|
||||
} *A = NULL;
|
||||
|
||||
static struct arena_chunk *alloc_arena_chunk(size_t sz, struct arena_chunk *prev) {
|
||||
struct arena_chunk *c = (struct arena_chunk *)calloc(1, sizeof(*c));
|
||||
if (!c)
|
||||
abort();
|
||||
c->sz = sz;
|
||||
c->ptr = calloc(1, sz);
|
||||
if (!c->ptr)
|
||||
abort();
|
||||
c->prev = prev;
|
||||
return c;
|
||||
}
|
||||
|
||||
void cmark_arena_push(void) {
|
||||
CMARK_INITIALIZE_AND_LOCK(arena);
|
||||
if (A) {
|
||||
A->push_point = 1;
|
||||
A = alloc_arena_chunk(10240, A);
|
||||
}
|
||||
CMARK_UNLOCK(arena);
|
||||
}
|
||||
|
||||
int cmark_arena_pop(void) {
|
||||
int ret = 1;
|
||||
CMARK_INITIALIZE_AND_LOCK(arena);
|
||||
if (!A)
|
||||
ret = 0;
|
||||
else {
|
||||
while (A && !A->push_point) {
|
||||
free(A->ptr);
|
||||
struct arena_chunk *n = A->prev;
|
||||
free(A);
|
||||
A = n;
|
||||
}
|
||||
if (A)
|
||||
A->push_point = 0;
|
||||
}
|
||||
CMARK_UNLOCK(arena);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void init_arena(void) {
|
||||
CMARK_INITIALIZE_AND_LOCK(arena);
|
||||
A = alloc_arena_chunk(4 * 1048576, NULL);
|
||||
CMARK_UNLOCK(arena);
|
||||
}
|
||||
|
||||
void cmark_arena_reset(void) {
|
||||
CMARK_INITIALIZE_AND_LOCK(arena);
|
||||
while (A) {
|
||||
free(A->ptr);
|
||||
struct arena_chunk *n = A->prev;
|
||||
free(A);
|
||||
A = n;
|
||||
}
|
||||
CMARK_UNLOCK(arena);
|
||||
}
|
||||
|
||||
static void *arena_calloc(size_t nmem, size_t size) {
|
||||
if (!A)
|
||||
init_arena();
|
||||
|
||||
size_t sz = nmem * size + sizeof(size_t);
|
||||
|
||||
// Round allocation sizes to largest integer size to
|
||||
// ensure returned memory is correctly aligned
|
||||
const size_t align = sizeof(size_t) - 1;
|
||||
sz = (sz + align) & ~align;
|
||||
|
||||
CMARK_INITIALIZE_AND_LOCK(arena);
|
||||
|
||||
struct arena_chunk *chunk;
|
||||
if (sz > A->sz) {
|
||||
A->prev = chunk = alloc_arena_chunk(sz, A->prev);
|
||||
} else if (sz > A->sz - A->used) {
|
||||
A = chunk = alloc_arena_chunk(A->sz + A->sz / 2, A);
|
||||
} else {
|
||||
chunk = A;
|
||||
}
|
||||
void *ptr = (uint8_t *) chunk->ptr + chunk->used;
|
||||
chunk->used += sz;
|
||||
*((size_t *) ptr) = sz - sizeof(size_t);
|
||||
|
||||
CMARK_UNLOCK(arena);
|
||||
|
||||
return (uint8_t *) ptr + sizeof(size_t);
|
||||
}
|
||||
|
||||
static void *arena_realloc(void *ptr, size_t size) {
|
||||
if (!A)
|
||||
init_arena();
|
||||
|
||||
void *new_ptr = arena_calloc(1, size);
|
||||
if (ptr)
|
||||
memcpy(new_ptr, ptr, ((size_t *) ptr)[-1]);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
static void arena_free(void *ptr) {
|
||||
(void) ptr;
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
cmark_mem CMARK_ARENA_MEM_ALLOCATOR = {arena_calloc, arena_realloc, arena_free};
|
||||
|
||||
cmark_mem *cmark_get_arena_mem_allocator(void) {
|
||||
return &CMARK_ARENA_MEM_ALLOCATOR;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,277 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark_ctype.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/* Used as default value for cmark_strbuf->ptr so that people can always
|
||||
* assume ptr is non-NULL and zero terminated even for new cmark_strbufs.
|
||||
*/
|
||||
unsigned char cmark_strbuf__initbuf[1];
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x < y) ? x : y)
|
||||
#endif
|
||||
|
||||
void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
|
||||
bufsize_t initial_size) {
|
||||
buf->mem = mem;
|
||||
buf->asize = 0;
|
||||
buf->size = 0;
|
||||
buf->ptr = cmark_strbuf__initbuf;
|
||||
|
||||
if (initial_size > 0)
|
||||
cmark_strbuf_grow(buf, initial_size);
|
||||
}
|
||||
|
||||
static inline void S_strbuf_grow_by(cmark_strbuf *buf, bufsize_t add) {
|
||||
cmark_strbuf_grow(buf, buf->size + add);
|
||||
}
|
||||
|
||||
void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) {
|
||||
assert(target_size > 0);
|
||||
|
||||
if (target_size < buf->asize)
|
||||
return;
|
||||
|
||||
if (target_size > (bufsize_t)(INT32_MAX / 2)) {
|
||||
fprintf(stderr,
|
||||
"[cmark] cmark_strbuf_grow requests buffer with size > %d, aborting\n",
|
||||
(INT32_MAX / 2));
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Oversize the buffer by 50% to guarantee amortized linear time
|
||||
* complexity on append operations. */
|
||||
bufsize_t new_size = target_size + target_size / 2;
|
||||
new_size += 1;
|
||||
new_size = (new_size + 7) & ~7;
|
||||
|
||||
buf->ptr = (unsigned char *)buf->mem->realloc(buf->asize ? buf->ptr : NULL,
|
||||
new_size);
|
||||
buf->asize = new_size;
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; }
|
||||
|
||||
void cmark_strbuf_free(cmark_strbuf *buf) {
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
if (buf->ptr != cmark_strbuf__initbuf)
|
||||
buf->mem->free(buf->ptr);
|
||||
|
||||
cmark_strbuf_init(buf->mem, buf, 0);
|
||||
}
|
||||
|
||||
void cmark_strbuf_clear(cmark_strbuf *buf) {
|
||||
buf->size = 0;
|
||||
|
||||
if (buf->asize > 0)
|
||||
buf->ptr[0] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len) {
|
||||
if (len <= 0 || data == NULL) {
|
||||
cmark_strbuf_clear(buf);
|
||||
} else {
|
||||
if (data != buf->ptr) {
|
||||
if (len >= buf->asize)
|
||||
cmark_strbuf_grow(buf, len);
|
||||
memmove(buf->ptr, data, len);
|
||||
}
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) {
|
||||
cmark_strbuf_set(buf, (const unsigned char *)string,
|
||||
string ? (bufsize_t)strlen(string) : 0);
|
||||
}
|
||||
|
||||
void cmark_strbuf_putc(cmark_strbuf *buf, int c) {
|
||||
S_strbuf_grow_by(buf, 1);
|
||||
buf->ptr[buf->size++] = (unsigned char)(c & 0xFF);
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len) {
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
S_strbuf_grow_by(buf, len);
|
||||
memmove(buf->ptr + buf->size, data, len);
|
||||
buf->size += len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) {
|
||||
cmark_strbuf_put(buf, (const unsigned char *)string, (bufsize_t)strlen(string));
|
||||
}
|
||||
|
||||
void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
|
||||
const cmark_strbuf *buf) {
|
||||
bufsize_t copylen;
|
||||
|
||||
assert(buf);
|
||||
if (!data || datasize <= 0)
|
||||
return;
|
||||
|
||||
data[0] = '\0';
|
||||
|
||||
if (buf->size == 0 || buf->asize <= 0)
|
||||
return;
|
||||
|
||||
copylen = buf->size;
|
||||
if (copylen > datasize - 1)
|
||||
copylen = datasize - 1;
|
||||
memmove(data, buf->ptr, copylen);
|
||||
data[copylen] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b) {
|
||||
cmark_strbuf t = *buf_a;
|
||||
*buf_a = *buf_b;
|
||||
*buf_b = t;
|
||||
}
|
||||
|
||||
unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) {
|
||||
unsigned char *data = buf->ptr;
|
||||
|
||||
if (buf->asize == 0) {
|
||||
/* return an empty string */
|
||||
return (unsigned char *)buf->mem->calloc(1, 1);
|
||||
}
|
||||
|
||||
cmark_strbuf_init(buf->mem, buf, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) {
|
||||
int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size));
|
||||
return (result != 0) ? result
|
||||
: (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
|
||||
if (pos >= buf->size)
|
||||
return -1;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
const unsigned char *p =
|
||||
(unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
return (bufsize_t)(p - (const unsigned char *)buf->ptr);
|
||||
}
|
||||
|
||||
bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos) {
|
||||
if (pos < 0 || buf->size == 0)
|
||||
return -1;
|
||||
if (pos >= buf->size)
|
||||
pos = buf->size - 1;
|
||||
|
||||
bufsize_t i;
|
||||
for (i = pos; i >= 0; i--) {
|
||||
if (buf->ptr[i] == (unsigned char)c)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len) {
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
|
||||
if (len < buf->size) {
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n) {
|
||||
if (n > 0) {
|
||||
if (n > buf->size)
|
||||
n = buf->size;
|
||||
buf->size = buf->size - n;
|
||||
if (buf->size)
|
||||
memmove(buf->ptr, buf->ptr + n, buf->size);
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_strbuf_rtrim(cmark_strbuf *buf) {
|
||||
if (!buf->size)
|
||||
return;
|
||||
|
||||
while (buf->size > 0) {
|
||||
if (!cmark_isspace(buf->ptr[buf->size - 1]))
|
||||
break;
|
||||
|
||||
buf->size--;
|
||||
}
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void cmark_strbuf_trim(cmark_strbuf *buf) {
|
||||
bufsize_t i = 0;
|
||||
|
||||
if (!buf->size)
|
||||
return;
|
||||
|
||||
while (i < buf->size && cmark_isspace(buf->ptr[i]))
|
||||
i++;
|
||||
|
||||
cmark_strbuf_drop(buf, i);
|
||||
|
||||
cmark_strbuf_rtrim(buf);
|
||||
}
|
||||
|
||||
// Destructively modify string, collapsing consecutive
|
||||
// space and newline characters into a single space.
|
||||
void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) {
|
||||
bool last_char_was_space = false;
|
||||
bufsize_t r, w;
|
||||
|
||||
for (r = 0, w = 0; r < s->size; ++r) {
|
||||
if (cmark_isspace(s->ptr[r])) {
|
||||
if (!last_char_was_space) {
|
||||
s->ptr[w++] = ' ';
|
||||
last_char_was_space = true;
|
||||
}
|
||||
} else {
|
||||
s->ptr[w++] = s->ptr[r];
|
||||
last_char_was_space = false;
|
||||
}
|
||||
}
|
||||
|
||||
cmark_strbuf_truncate(s, w);
|
||||
}
|
||||
|
||||
// Destructively unescape a string: remove backslashes before punctuation chars.
|
||||
extern void cmark_strbuf_unescape(cmark_strbuf *buf) {
|
||||
bufsize_t r, w;
|
||||
|
||||
for (r = 0, w = 0; r < buf->size; ++r) {
|
||||
if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1]))
|
||||
r++;
|
||||
|
||||
buf->ptr[w++] = buf->ptr[r];
|
||||
}
|
||||
|
||||
cmark_strbuf_truncate(buf, w);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "registry.h"
|
||||
#include "node.h"
|
||||
#include "houdini.h"
|
||||
#include "cmark-gfm.h"
|
||||
#include "buffer.h"
|
||||
|
||||
cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION;
|
||||
cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_ATTRIBUTE;
|
||||
|
||||
int cmark_version(void) { return CMARK_GFM_VERSION; }
|
||||
|
||||
const char *cmark_version_string(void) { return CMARK_GFM_VERSION_STRING; }
|
||||
|
||||
static void *xcalloc(size_t nmem, size_t size) {
|
||||
void *ptr = calloc(nmem, size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n");
|
||||
abort();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *xrealloc(void *ptr, size_t size) {
|
||||
void *new_ptr = realloc(ptr, size);
|
||||
if (!new_ptr) {
|
||||
fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n");
|
||||
abort();
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
static void xfree(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, xfree};
|
||||
|
||||
cmark_mem *cmark_get_default_mem_allocator(void) {
|
||||
return &CMARK_DEFAULT_MEM_ALLOCATOR;
|
||||
}
|
||||
|
||||
char *cmark_markdown_to_html(const char *text, size_t len, int options) {
|
||||
cmark_node *doc;
|
||||
char *result;
|
||||
|
||||
doc = cmark_parse_document(text, len, options);
|
||||
|
||||
result = cmark_render_html(doc, options, NULL);
|
||||
cmark_node_free(doc);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cmark_ctype.h"
|
||||
|
||||
/** 1 = space, 2 = punct, 3 = digit, 4 = alpha, 0 = other
|
||||
*/
|
||||
static const uint8_t cmark_ctype_class[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
|
||||
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
|
||||
/* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 2 */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
/* 3 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
|
||||
/* 4 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
/* 5 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2,
|
||||
/* 6 */ 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
/* 7 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0,
|
||||
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
/**
|
||||
* Returns 1 if c is a "whitespace" character as defined by the spec.
|
||||
*/
|
||||
int cmark_isspace(char c) { return cmark_ctype_class[(uint8_t)c] == 1; }
|
||||
|
||||
/**
|
||||
* Returns 1 if c is an ascii punctuation character.
|
||||
*/
|
||||
int cmark_ispunct(char c) { return cmark_ctype_class[(uint8_t)c] == 2; }
|
||||
|
||||
int cmark_isalnum(char c) {
|
||||
uint8_t result;
|
||||
result = cmark_ctype_class[(uint8_t)c];
|
||||
return (result == 3 || result == 4);
|
||||
}
|
||||
|
||||
int cmark_isdigit(char c) { return cmark_ctype_class[(uint8_t)c] == 3; }
|
||||
|
||||
int cmark_isalpha(char c) { return cmark_ctype_class[(uint8_t)c] == 4; }
|
||||
@@ -1,524 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "scanners.h"
|
||||
#include "render.h"
|
||||
#include "syntax_extension.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, node, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define ENCODED_SIZE 20
|
||||
#define LISTMARKER_SIZE 20
|
||||
|
||||
// Functions to convert cmark_nodes to commonmark strings.
|
||||
|
||||
static inline void outc(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_escaping escape, int32_t c, unsigned char nextc) {
|
||||
bool needs_escaping = false;
|
||||
bool follows_digit =
|
||||
renderer->buffer->size > 0 &&
|
||||
cmark_isdigit(renderer->buffer->ptr[renderer->buffer->size - 1]);
|
||||
char encoded[ENCODED_SIZE];
|
||||
|
||||
needs_escaping =
|
||||
c < 0x80 && escape != LITERAL &&
|
||||
((escape == NORMAL &&
|
||||
(c < 0x20 ||
|
||||
c == '*' || c == '_' || c == '[' || c == ']' || c == '#' || c == '<' ||
|
||||
c == '>' || c == '\\' || c == '`' || c == '~' || c == '!' ||
|
||||
(c == '&' && cmark_isalpha(nextc)) || (c == '!' && nextc == '[') ||
|
||||
(c == '^' && nextc == '[') ||
|
||||
(renderer->begin_content && (c == '-' || c == '+' || c == '=') &&
|
||||
// begin_content doesn't get set to false til we've passed digits
|
||||
// at the beginning of line, so...
|
||||
!follows_digit) ||
|
||||
(renderer->begin_content && (c == '.' || c == ')') && follows_digit &&
|
||||
(nextc == 0 || cmark_isspace(nextc))))) ||
|
||||
(escape == URL &&
|
||||
(c == '`' || c == '<' || c == '>' || cmark_isspace((char)c) || c == '\\' ||
|
||||
c == ')' || c == '(')) ||
|
||||
(escape == TITLE &&
|
||||
(c == '`' || c == '<' || c == '>' || c == '"' || c == '\\')));
|
||||
|
||||
if (needs_escaping) {
|
||||
if (escape == URL && cmark_isspace((char)c)) {
|
||||
// use percent encoding for spaces
|
||||
snprintf(encoded, ENCODED_SIZE, "%%%2X", c);
|
||||
cmark_strbuf_puts(renderer->buffer, encoded);
|
||||
renderer->column += 3;
|
||||
} else if (cmark_ispunct((char)c)) {
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
cmark_render_code_point(renderer, c);
|
||||
} else { // render as entity
|
||||
snprintf(encoded, ENCODED_SIZE, "&#%d;", c);
|
||||
cmark_strbuf_puts(renderer->buffer, encoded);
|
||||
renderer->column += (int)strlen(encoded);
|
||||
}
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
static int longest_backtick_sequence(const char *code) {
|
||||
int longest = 0;
|
||||
int current = 0;
|
||||
size_t i = 0;
|
||||
size_t code_len = strlen(code);
|
||||
while (i <= code_len) {
|
||||
if (code[i] == '`') {
|
||||
current++;
|
||||
} else {
|
||||
if (current > longest) {
|
||||
longest = current;
|
||||
}
|
||||
current = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return longest;
|
||||
}
|
||||
|
||||
static int shortest_unused_backtick_sequence(const char *code) {
|
||||
// note: if the shortest sequence is >= 32, this returns 32
|
||||
// so as not to overflow the bit array.
|
||||
uint32_t used = 1;
|
||||
int current = 0;
|
||||
size_t i = 0;
|
||||
size_t code_len = strlen(code);
|
||||
while (i <= code_len) {
|
||||
if (code[i] == '`') {
|
||||
current++;
|
||||
} else {
|
||||
if (current > 0 && current < 32) {
|
||||
used |= (1U << current);
|
||||
}
|
||||
current = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// return number of first bit that is 0:
|
||||
i = 0;
|
||||
while (i < 32 && used & 1) {
|
||||
used = used >> 1;
|
||||
i++;
|
||||
}
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
static bool is_autolink(cmark_node *node) {
|
||||
cmark_chunk *title;
|
||||
cmark_chunk *url;
|
||||
cmark_node *link_text;
|
||||
char *realurl;
|
||||
int realurllen;
|
||||
|
||||
if (node->type != CMARK_NODE_LINK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
url = &node->as.link.url;
|
||||
if (url->len == 0 || scan_scheme(url, 0) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
title = &node->as.link.title;
|
||||
// if it has a title, we can't treat it as an autolink:
|
||||
if (title->len > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
link_text = node->first_child;
|
||||
if (link_text == NULL) {
|
||||
return false;
|
||||
}
|
||||
cmark_consolidate_text_nodes(link_text);
|
||||
realurl = (char *)url->data;
|
||||
realurllen = url->len;
|
||||
if (strncmp(realurl, "mailto:", 7) == 0) {
|
||||
realurl += 7;
|
||||
realurllen -= 7;
|
||||
}
|
||||
return (realurllen == link_text->as.literal.len &&
|
||||
strncmp(realurl, (char *)link_text->as.literal.data,
|
||||
link_text->as.literal.len) == 0);
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
int list_number;
|
||||
cmark_delim_type list_delim;
|
||||
int numticks;
|
||||
bool extra_spaces;
|
||||
int i;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
const char *info, *code, *title;
|
||||
char fencechar[2] = {'\0', '\0'};
|
||||
size_t info_len, code_len;
|
||||
char listmarker[LISTMARKER_SIZE];
|
||||
const char *emph_delim;
|
||||
bool first_in_list_item;
|
||||
bufsize_t marker_width;
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
|
||||
!(CMARK_OPT_HARDBREAKS & options);
|
||||
|
||||
// Don't adjust tight list status til we've started the list.
|
||||
// Otherwise we loose the blank line between a paragraph and
|
||||
// a following list.
|
||||
if (entering) {
|
||||
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
|
||||
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
|
||||
}
|
||||
} else {
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
renderer->in_tight_list_item =
|
||||
node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM &&
|
||||
node->parent->parent->as.list.tight;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->extension && node->extension->commonmark_render_func) {
|
||||
node->extension->commonmark_render_func(node->extension, renderer, node, ev_type, options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
LIT("> ");
|
||||
renderer->begin_content = true;
|
||||
cmark_strbuf_puts(renderer->prefix, "> ");
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2);
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK ||
|
||||
node->next->type == CMARK_NODE_LIST)) {
|
||||
// this ensures that a following indented code block or list will be
|
||||
// inteprereted correctly.
|
||||
CR();
|
||||
LIT("<!-- end list -->");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
marker_width = 4;
|
||||
} else {
|
||||
list_number = cmark_node_get_item_index(node);
|
||||
list_delim = cmark_node_get_list_delim(node->parent);
|
||||
// we ensure a width of at least 4 so
|
||||
// we get nice transition from single digits
|
||||
// to double
|
||||
snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number,
|
||||
list_delim == CMARK_PAREN_DELIM ? ")" : ".",
|
||||
list_number < 10 ? " " : " ");
|
||||
marker_width = (bufsize_t)strlen(listmarker);
|
||||
}
|
||||
if (entering) {
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
LIT(" - ");
|
||||
renderer->begin_content = true;
|
||||
} else {
|
||||
LIT(listmarker);
|
||||
renderer->begin_content = true;
|
||||
}
|
||||
for (i = marker_width; i--;) {
|
||||
cmark_strbuf_putc(renderer->prefix, ' ');
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix,
|
||||
renderer->prefix->size - marker_width);
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
for (i = cmark_node_get_heading_level(node); i > 0; i--) {
|
||||
LIT("#");
|
||||
}
|
||||
LIT(" ");
|
||||
renderer->begin_content = true;
|
||||
renderer->no_linebreaks = true;
|
||||
} else {
|
||||
renderer->no_linebreaks = false;
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
first_in_list_item = node->prev == NULL && node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM;
|
||||
|
||||
if (!first_in_list_item) {
|
||||
BLANKLINE();
|
||||
}
|
||||
info = cmark_node_get_fence_info(node);
|
||||
info_len = strlen(info);
|
||||
fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
|
||||
code = cmark_node_get_literal(node);
|
||||
code_len = strlen(code);
|
||||
// use indented form if no info, and code doesn't
|
||||
// begin or end with a blank line, and code isn't
|
||||
// first thing in a list item
|
||||
if (info_len == 0 && (code_len > 2 && !cmark_isspace(code[0]) &&
|
||||
!(cmark_isspace(code[code_len - 1]) &&
|
||||
cmark_isspace(code[code_len - 2]))) &&
|
||||
!first_in_list_item) {
|
||||
LIT(" ");
|
||||
cmark_strbuf_puts(renderer->prefix, " ");
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
|
||||
} else {
|
||||
numticks = longest_backtick_sequence(code) + 1;
|
||||
if (numticks < 3) {
|
||||
numticks = 3;
|
||||
}
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT(fencechar);
|
||||
}
|
||||
LIT(" ");
|
||||
OUT(info, false, LITERAL);
|
||||
CR();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
CR();
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT(fencechar);
|
||||
}
|
||||
}
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
BLANKLINE();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
BLANKLINE();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
BLANKLINE();
|
||||
LIT("-----");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (!entering) {
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
if (!(CMARK_OPT_HARDBREAKS & options)) {
|
||||
LIT(" ");
|
||||
}
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (CMARK_OPT_HARDBREAKS & options) {
|
||||
LIT(" ");
|
||||
CR();
|
||||
} else if (!renderer->no_linebreaks && renderer->width == 0 &&
|
||||
!(CMARK_OPT_HARDBREAKS & options) &&
|
||||
!(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, LITERAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
code = cmark_node_get_literal(node);
|
||||
code_len = strlen(code);
|
||||
numticks = shortest_unused_backtick_sequence(code);
|
||||
extra_spaces = code_len == 0 ||
|
||||
code[0] == '`' || code[code_len - 1] == '`' ||
|
||||
code[0] == ' ' || code[code_len - 1] == ' ';
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT("`");
|
||||
}
|
||||
if (extra_spaces) {
|
||||
LIT(" ");
|
||||
}
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
|
||||
if (extra_spaces) {
|
||||
LIT(" ");
|
||||
}
|
||||
for (i = 0; i < numticks; i++) {
|
||||
LIT("`");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
||||
if (entering) {
|
||||
LIT("**");
|
||||
} else {
|
||||
LIT("**");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
// If we have EMPH(EMPH(x)), we need to use *_x_*
|
||||
// because **x** is STRONG(x):
|
||||
if (node->parent && node->parent->type == CMARK_NODE_EMPH &&
|
||||
node->next == NULL && node->prev == NULL) {
|
||||
emph_delim = "_";
|
||||
} else {
|
||||
emph_delim = "*";
|
||||
}
|
||||
if (entering) {
|
||||
LIT(emph_delim);
|
||||
} else {
|
||||
LIT(emph_delim);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (is_autolink(node)) {
|
||||
if (entering) {
|
||||
LIT("<");
|
||||
if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) {
|
||||
LIT((const char *)cmark_node_get_url(node) + 7);
|
||||
} else {
|
||||
LIT((const char *)cmark_node_get_url(node));
|
||||
}
|
||||
LIT(">");
|
||||
// return signal to skip contents of node...
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (entering) {
|
||||
LIT("[");
|
||||
} else {
|
||||
LIT("](");
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
title = cmark_node_get_title(node);
|
||||
if (strlen(title) > 0) {
|
||||
LIT(" \"");
|
||||
OUT(title, false, TITLE);
|
||||
LIT("\"");
|
||||
}
|
||||
LIT(")");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT(";
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
title = cmark_node_get_title(node);
|
||||
if (strlen(title) > 0) {
|
||||
OUT(" \"", allow_wrap, LITERAL);
|
||||
OUT(title, false, TITLE);
|
||||
LIT("\"");
|
||||
}
|
||||
LIT(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ATTRIBUTE:
|
||||
if (entering) {
|
||||
LIT("^[");
|
||||
} else {
|
||||
LIT("](");
|
||||
OUT(cmark_node_get_attributes(node), false, LITERAL);
|
||||
LIT(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
||||
if (entering) {
|
||||
LIT("[^");
|
||||
|
||||
char *footnote_label = renderer->mem->calloc(node->parent_footnote_def->as.literal.len + 1, sizeof(char));
|
||||
memmove(footnote_label, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
||||
|
||||
OUT(footnote_label, false, LITERAL);
|
||||
renderer->mem->free(footnote_label);
|
||||
|
||||
LIT("]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
||||
if (entering) {
|
||||
renderer->footnote_ix += 1;
|
||||
LIT("[^");
|
||||
|
||||
char *footnote_label = renderer->mem->calloc(node->as.literal.len + 1, sizeof(char));
|
||||
memmove(footnote_label, node->as.literal.data, node->as.literal.len);
|
||||
|
||||
OUT(footnote_label, false, LITERAL);
|
||||
renderer->mem->free(footnote_label);
|
||||
|
||||
LIT("]:\n");
|
||||
|
||||
cmark_strbuf_puts(renderer->prefix, " ");
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_commonmark(cmark_node *root, int options, int width) {
|
||||
return cmark_render_commonmark_with_mem(root, options, width, cmark_node_mem(root));
|
||||
}
|
||||
|
||||
char *cmark_render_commonmark_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) {
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
// disable breaking on width, since it has
|
||||
// a different meaning with OPT_HARDBREAKS
|
||||
width = 0;
|
||||
}
|
||||
return cmark_render(mem, root, options, width, outc, S_render_node);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
#include "cmark-gfm.h"
|
||||
#include "parser.h"
|
||||
#include "footnotes.h"
|
||||
#include "inlines.h"
|
||||
#include "chunk.h"
|
||||
|
||||
static void footnote_free(cmark_map *map, cmark_map_entry *_ref) {
|
||||
cmark_footnote *ref = (cmark_footnote *)_ref;
|
||||
cmark_mem *mem = map->mem;
|
||||
if (ref != NULL) {
|
||||
mem->free(ref->entry.label);
|
||||
if (ref->node)
|
||||
cmark_node_free(ref->node);
|
||||
mem->free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_footnote_create(cmark_map *map, cmark_node *node) {
|
||||
cmark_footnote *ref;
|
||||
unsigned char *reflabel = normalize_map_label(map->mem, &node->as.literal);
|
||||
|
||||
/* empty footnote name, or composed from only whitespace */
|
||||
if (reflabel == NULL)
|
||||
return;
|
||||
|
||||
assert(map->sorted == NULL);
|
||||
|
||||
ref = (cmark_footnote *)map->mem->calloc(1, sizeof(*ref));
|
||||
ref->entry.label = reflabel;
|
||||
ref->node = node;
|
||||
ref->entry.age = map->size;
|
||||
ref->entry.next = map->refs;
|
||||
|
||||
map->refs = (cmark_map_entry *)ref;
|
||||
map->size++;
|
||||
}
|
||||
|
||||
cmark_map *cmark_footnote_map_new(cmark_mem *mem) {
|
||||
return cmark_map_new(mem, footnote_free);
|
||||
}
|
||||
|
||||
// Before calling `cmark_map_free` on a map with `cmark_footnotes`, first
|
||||
// unlink all of the footnote nodes before freeing their memory.
|
||||
//
|
||||
// Sometimes, two (unused) footnote nodes can end up referencing each other,
|
||||
// which as they get freed up by calling `cmark_map_free` -> `footnote_free` ->
|
||||
// etc, can lead to a use-after-free error.
|
||||
//
|
||||
// Better to `unlink` every footnote node first, setting their next, prev, and
|
||||
// parent pointers to NULL, and only then walk thru & free them up.
|
||||
void cmark_unlink_footnotes_map(cmark_map *map) {
|
||||
cmark_map_entry *ref;
|
||||
cmark_map_entry *next;
|
||||
|
||||
ref = map->refs;
|
||||
while(ref) {
|
||||
next = ref->next;
|
||||
if (((cmark_footnote *)ref)->node) {
|
||||
cmark_node_unlink(((cmark_footnote *)ref)->node);
|
||||
}
|
||||
ref = next;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "houdini.h"
|
||||
|
||||
#if !defined(__has_builtin)
|
||||
# define __has_builtin(b) 0
|
||||
#endif
|
||||
|
||||
#if !__has_builtin(__builtin_expect)
|
||||
# define __builtin_expect(e, v) (e)
|
||||
#endif
|
||||
|
||||
#define likely(e) __builtin_expect((e), 1)
|
||||
|
||||
/*
|
||||
* The following characters will not be escaped:
|
||||
*
|
||||
* -_.+!*'(),%#@?=;:/,+&$~ alphanum
|
||||
*
|
||||
* Note that this character set is the addition of:
|
||||
*
|
||||
* - The characters which are safe to be in an URL
|
||||
* - The characters which are *not* safe to be in
|
||||
* an URL because they are RESERVED characters.
|
||||
*
|
||||
* We assume (lazily) that any RESERVED char that
|
||||
* appears inside an URL is actually meant to
|
||||
* have its native function (i.e. as an URL
|
||||
* component/separator) and hence needs no escaping.
|
||||
*
|
||||
* There are two exceptions: the chacters & (amp)
|
||||
* and ' (single quote) do not appear in the table.
|
||||
* They are meant to appear in the URL as components,
|
||||
* yet they require special HTML-entity escaping
|
||||
* to generate valid HTML markup.
|
||||
*
|
||||
* All other characters will be escaped to %XX.
|
||||
*
|
||||
*/
|
||||
static const char HREF_SAFE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
|
||||
static const uint8_t hex_chars[] = "0123456789ABCDEF";
|
||||
bufsize_t i = 0, org;
|
||||
uint8_t hex_str[3];
|
||||
|
||||
hex_str[0] = '%';
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && HREF_SAFE[src[i]] != 0)
|
||||
i++;
|
||||
|
||||
if (likely(i > org))
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
|
||||
/* escaping */
|
||||
if (i >= size)
|
||||
break;
|
||||
|
||||
switch (src[i]) {
|
||||
/* amp appears all the time in URLs, but needs
|
||||
* HTML-entity escaping to be inside an href */
|
||||
case '&':
|
||||
cmark_strbuf_puts(ob, "&");
|
||||
break;
|
||||
|
||||
/* the single quote is a valid URL character
|
||||
* according to the standard; it needs HTML
|
||||
* entity escaping too */
|
||||
case '\'':
|
||||
cmark_strbuf_puts(ob, "'");
|
||||
break;
|
||||
|
||||
/* the space can be escaped to %20 or a plus
|
||||
* sign. we're going with the generic escape
|
||||
* for now. the plus thing is more commonly seen
|
||||
* when building GET strings */
|
||||
#if 0
|
||||
case ' ':
|
||||
cmark_strbuf_putc(ob, '+');
|
||||
break;
|
||||
#endif
|
||||
|
||||
/* every other character goes with a %XX escaping */
|
||||
default:
|
||||
hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
|
||||
hex_str[2] = hex_chars[src[i] & 0xF];
|
||||
cmark_strbuf_put(ob, hex_str, 3);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "houdini.h"
|
||||
|
||||
#if !defined(__has_builtin)
|
||||
# define __has_builtin(b) 0
|
||||
#endif
|
||||
|
||||
#if !__has_builtin(__builtin_expect)
|
||||
# define __builtin_expect(e, v) (e)
|
||||
#endif
|
||||
|
||||
#define unlikely(e) __builtin_expect((e), 0)
|
||||
|
||||
/**
|
||||
* According to the OWASP rules:
|
||||
*
|
||||
* & --> &
|
||||
* < --> <
|
||||
* > --> >
|
||||
* " --> "
|
||||
* ' --> ' ' is not recommended
|
||||
* / --> / forward slash is included as it helps end an HTML entity
|
||||
*
|
||||
*/
|
||||
static const char HTML_ESCAPE_TABLE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static const char *HTML_ESCAPES[] = {"", """, "&", "'",
|
||||
"/", "<", ">"};
|
||||
|
||||
int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src, bufsize_t size,
|
||||
int secure) {
|
||||
bufsize_t i = 0, org, esc = 0;
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
|
||||
i++;
|
||||
|
||||
if (i > org)
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
|
||||
/* escaping */
|
||||
if (unlikely(i >= size))
|
||||
break;
|
||||
|
||||
/* The forward slash and single quote are only escaped in secure mode */
|
||||
if ((src[i] == '/' || src[i] == '\'') && !secure) {
|
||||
cmark_strbuf_putc(ob, src[i]);
|
||||
} else {
|
||||
cmark_strbuf_puts(ob, HTML_ESCAPES[esc]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src, bufsize_t size) {
|
||||
return houdini_escape_html0(ob, src, size, 1);
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "houdini.h"
|
||||
#include "utf8.h"
|
||||
#include "entities.inc"
|
||||
|
||||
#if !defined(__has_builtin)
|
||||
# define __has_builtin(b) 0
|
||||
#endif
|
||||
|
||||
#if !__has_builtin(__builtin_expect)
|
||||
# define __builtin_expect(e, v) (e)
|
||||
#endif
|
||||
|
||||
#define likely(e) __builtin_expect((e), 1)
|
||||
#define unlikely(e) __builtin_expect((e), 0)
|
||||
|
||||
/* Binary tree lookup code for entities added by JGM */
|
||||
|
||||
static const unsigned char *S_lookup(int i, int low, int hi,
|
||||
const unsigned char *s, int len) {
|
||||
int j;
|
||||
int cmp =
|
||||
strncmp((const char *)s, (const char *)cmark_entities[i].entity, len);
|
||||
if (cmp == 0 && cmark_entities[i].entity[len] == 0) {
|
||||
return (const unsigned char *)cmark_entities[i].bytes;
|
||||
} else if (cmp <= 0 && i > low) {
|
||||
j = i - ((i - low) / 2);
|
||||
if (j == i)
|
||||
j -= 1;
|
||||
return S_lookup(j, low, i - 1, s, len);
|
||||
} else if (cmp > 0 && i < hi) {
|
||||
j = i + ((hi - i) / 2);
|
||||
if (j == i)
|
||||
j += 1;
|
||||
return S_lookup(j, i + 1, hi, s, len);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char *S_lookup_entity(const unsigned char *s, int len) {
|
||||
return S_lookup(CMARK_NUM_ENTITIES / 2, 0, CMARK_NUM_ENTITIES - 1, s, len);
|
||||
}
|
||||
|
||||
bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0;
|
||||
|
||||
if (size >= 3 && src[0] == '#') {
|
||||
int codepoint = 0;
|
||||
int num_digits = 0;
|
||||
|
||||
if (_isdigit(src[1])) {
|
||||
for (i = 1; i < size && _isdigit(src[i]); ++i) {
|
||||
codepoint = (codepoint * 10) + (src[i] - '0');
|
||||
|
||||
if (codepoint >= 0x110000) {
|
||||
// Keep counting digits but
|
||||
// avoid integer overflow.
|
||||
codepoint = 0x110000;
|
||||
}
|
||||
}
|
||||
|
||||
num_digits = i - 1;
|
||||
}
|
||||
|
||||
else if (src[1] == 'x' || src[1] == 'X') {
|
||||
for (i = 2; i < size && _isxdigit(src[i]); ++i) {
|
||||
codepoint = (codepoint * 16) + ((src[i] | 32) % 39 - 9);
|
||||
|
||||
if (codepoint >= 0x110000) {
|
||||
// Keep counting digits but
|
||||
// avoid integer overflow.
|
||||
codepoint = 0x110000;
|
||||
}
|
||||
}
|
||||
|
||||
num_digits = i - 2;
|
||||
}
|
||||
|
||||
if (num_digits >= 1 && num_digits <= 8 && i < size && src[i] == ';') {
|
||||
if (codepoint == 0 || (codepoint >= 0xD800 && codepoint < 0xE000) ||
|
||||
codepoint >= 0x110000) {
|
||||
codepoint = 0xFFFD;
|
||||
}
|
||||
cmark_utf8proc_encode_char(codepoint, ob);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (size > CMARK_ENTITY_MAX_LENGTH)
|
||||
size = CMARK_ENTITY_MAX_LENGTH;
|
||||
|
||||
for (i = CMARK_ENTITY_MIN_LENGTH; i < size; ++i) {
|
||||
if (src[i] == ' ')
|
||||
break;
|
||||
|
||||
if (src[i] == ';') {
|
||||
const unsigned char *entity = S_lookup_entity(src, i);
|
||||
|
||||
if (entity != NULL) {
|
||||
cmark_strbuf_puts(ob, (const char *)entity);
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
bufsize_t i = 0, org, ent;
|
||||
|
||||
while (i < size) {
|
||||
org = i;
|
||||
while (i < size && src[i] != '&')
|
||||
i++;
|
||||
|
||||
if (likely(i > org)) {
|
||||
if (unlikely(org == 0)) {
|
||||
if (i >= size)
|
||||
return 0;
|
||||
|
||||
cmark_strbuf_grow(ob, HOUDINI_UNESCAPED_SIZE(size));
|
||||
}
|
||||
|
||||
cmark_strbuf_put(ob, src + org, i - org);
|
||||
}
|
||||
|
||||
/* escaping */
|
||||
if (i >= size)
|
||||
break;
|
||||
|
||||
i++;
|
||||
|
||||
ent = houdini_unescape_ent(ob, src + i, size - i);
|
||||
i += ent;
|
||||
|
||||
/* not really an entity */
|
||||
if (ent == 0)
|
||||
cmark_strbuf_putc(ob, '&');
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size) {
|
||||
if (!houdini_unescape_html(ob, src, size))
|
||||
cmark_strbuf_put(ob, src, size);
|
||||
}
|
||||
@@ -1,516 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark_ctype.h"
|
||||
#include "cmark-gfm.h"
|
||||
#include "houdini.h"
|
||||
#include "scanners.h"
|
||||
#include "syntax_extension.h"
|
||||
#include "html.h"
|
||||
#include "render.h"
|
||||
|
||||
// Functions to convert cmark_nodes to HTML strings.
|
||||
|
||||
static void escape_html(cmark_strbuf *dest, const unsigned char *source,
|
||||
bufsize_t length) {
|
||||
houdini_escape_html0(dest, source, length, 0);
|
||||
}
|
||||
|
||||
static void filter_html_block(cmark_html_renderer *renderer, uint8_t *data, size_t len) {
|
||||
cmark_strbuf *html = renderer->html;
|
||||
cmark_llist *it;
|
||||
cmark_syntax_extension *ext;
|
||||
bool filtered;
|
||||
uint8_t *match;
|
||||
|
||||
while (len) {
|
||||
match = (uint8_t *) memchr(data, '<', len);
|
||||
if (!match)
|
||||
break;
|
||||
|
||||
if (match != data) {
|
||||
cmark_strbuf_put(html, data, (bufsize_t)(match - data));
|
||||
len -= (match - data);
|
||||
data = match;
|
||||
}
|
||||
|
||||
filtered = false;
|
||||
for (it = renderer->filter_extensions; it; it = it->next) {
|
||||
ext = ((cmark_syntax_extension *) it->data);
|
||||
if (!ext->html_filter_func(ext, data, len)) {
|
||||
filtered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered) {
|
||||
cmark_strbuf_putc(html, '<');
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "<");
|
||||
}
|
||||
|
||||
++data;
|
||||
--len;
|
||||
}
|
||||
|
||||
if (len)
|
||||
cmark_strbuf_put(html, data, (bufsize_t)len);
|
||||
}
|
||||
|
||||
static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html, cmark_node *node) {
|
||||
if (renderer->written_footnote_ix >= renderer->footnote_ix)
|
||||
return false;
|
||||
renderer->written_footnote_ix = renderer->footnote_ix;
|
||||
char m[32];
|
||||
snprintf(m, sizeof(m), "%d", renderer->written_footnote_ix);
|
||||
|
||||
cmark_strbuf_puts(html, "<a href=\"#fnref-");
|
||||
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
||||
cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"");
|
||||
cmark_strbuf_puts(html, m);
|
||||
cmark_strbuf_puts(html, "\" aria-label=\"Back to reference ");
|
||||
cmark_strbuf_puts(html, m);
|
||||
cmark_strbuf_puts(html, "\">↩</a>");
|
||||
|
||||
if (node->footnote.def_count > 1)
|
||||
{
|
||||
for(int i = 2; i <= node->footnote.def_count; i++) {
|
||||
char n[32];
|
||||
snprintf(n, sizeof(n), "%d", i);
|
||||
|
||||
cmark_strbuf_puts(html, " <a href=\"#fnref-");
|
||||
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
||||
cmark_strbuf_puts(html, "-");
|
||||
cmark_strbuf_puts(html, n);
|
||||
cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"");
|
||||
cmark_strbuf_puts(html, m);
|
||||
cmark_strbuf_puts(html, "-");
|
||||
cmark_strbuf_puts(html, n);
|
||||
cmark_strbuf_puts(html, "\" aria-label=\"Back to reference ");
|
||||
cmark_strbuf_puts(html, m);
|
||||
cmark_strbuf_puts(html, "-");
|
||||
cmark_strbuf_puts(html, n);
|
||||
cmark_strbuf_puts(html, "\">↩<sup class=\"footnote-ref\">");
|
||||
cmark_strbuf_puts(html, n);
|
||||
cmark_strbuf_puts(html, "</sup></a>");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
cmark_node *parent;
|
||||
cmark_node *grandparent;
|
||||
cmark_strbuf *html = renderer->html;
|
||||
cmark_llist *it;
|
||||
cmark_syntax_extension *ext;
|
||||
char start_heading[] = "<h0";
|
||||
char end_heading[] = "</h0";
|
||||
bool tight;
|
||||
bool filtered;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
|
||||
if (renderer->plain == node) { // back at original node
|
||||
renderer->plain = NULL;
|
||||
}
|
||||
|
||||
if (renderer->plain != NULL) {
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
escape_html(html, node->as.literal.data, node->as.literal.len);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
cmark_strbuf_putc(html, ' ');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (node->extension && node->extension->html_render_func) {
|
||||
node->extension->html_render_func(node->extension, renderer, node, ev_type, options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
cmark_html_render_cr(html);
|
||||
cmark_strbuf_puts(html, "<blockquote");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else {
|
||||
cmark_html_render_cr(html);
|
||||
cmark_strbuf_puts(html, "</blockquote>\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST: {
|
||||
cmark_list_type list_type = node->as.list.list_type;
|
||||
int start = node->as.list.start;
|
||||
|
||||
if (entering) {
|
||||
cmark_html_render_cr(html);
|
||||
if (list_type == CMARK_BULLET_LIST) {
|
||||
cmark_strbuf_puts(html, "<ul");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else if (start == 1) {
|
||||
cmark_strbuf_puts(html, "<ol");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
} else {
|
||||
snprintf(buffer, BUFFER_SIZE, "<ol start=\"%d\"", start);
|
||||
cmark_strbuf_puts(html, buffer);
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_puts(html,
|
||||
list_type == CMARK_BULLET_LIST ? "</ul>\n" : "</ol>\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
cmark_html_render_cr(html);
|
||||
cmark_strbuf_puts(html, "<li");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</li>\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
cmark_html_render_cr(html);
|
||||
start_heading[2] = (char)('0' + node->as.heading.level);
|
||||
cmark_strbuf_puts(html, start_heading);
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
end_heading[3] = (char)('0' + node->as.heading.level);
|
||||
cmark_strbuf_puts(html, end_heading);
|
||||
cmark_strbuf_puts(html, ">\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
cmark_html_render_cr(html);
|
||||
|
||||
if (node->as.code.info.len == 0) {
|
||||
cmark_strbuf_puts(html, "<pre");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, "><code>");
|
||||
} else {
|
||||
bufsize_t first_tag = 0;
|
||||
while (first_tag < node->as.code.info.len &&
|
||||
!cmark_isspace(node->as.code.info.data[first_tag])) {
|
||||
first_tag += 1;
|
||||
}
|
||||
|
||||
if (options & CMARK_OPT_GITHUB_PRE_LANG) {
|
||||
cmark_strbuf_puts(html, "<pre");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, " lang=\"");
|
||||
escape_html(html, node->as.code.info.data, first_tag);
|
||||
if (first_tag < node->as.code.info.len && (options & CMARK_OPT_FULL_INFO_STRING)) {
|
||||
cmark_strbuf_puts(html, "\" data-meta=\"");
|
||||
escape_html(html, node->as.code.info.data + first_tag + 1, node->as.code.info.len - first_tag - 1);
|
||||
}
|
||||
cmark_strbuf_puts(html, "\"><code>");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "<pre");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, "><code class=\"language-");
|
||||
escape_html(html, node->as.code.info.data, first_tag);
|
||||
if (first_tag < node->as.code.info.len && (options & CMARK_OPT_FULL_INFO_STRING)) {
|
||||
cmark_strbuf_puts(html, "\" data-meta=\"");
|
||||
escape_html(html, node->as.code.info.data + first_tag + 1, node->as.code.info.len - first_tag - 1);
|
||||
}
|
||||
cmark_strbuf_puts(html, "\">");
|
||||
}
|
||||
}
|
||||
|
||||
escape_html(html, node->as.code.literal.data, node->as.code.literal.len);
|
||||
cmark_strbuf_puts(html, "</code></pre>\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
cmark_html_render_cr(html);
|
||||
if (!(options & CMARK_OPT_UNSAFE)) {
|
||||
cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
|
||||
} else if (renderer->filter_extensions) {
|
||||
filter_html_block(renderer, node->as.literal.data, node->as.literal.len);
|
||||
} else {
|
||||
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
|
||||
}
|
||||
cmark_html_render_cr(html);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
cmark_html_render_cr(html);
|
||||
if (entering) {
|
||||
cmark_strbuf_put(html, node->as.custom.on_enter.data,
|
||||
node->as.custom.on_enter.len);
|
||||
} else {
|
||||
cmark_strbuf_put(html, node->as.custom.on_exit.data,
|
||||
node->as.custom.on_exit.len);
|
||||
}
|
||||
cmark_html_render_cr(html);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
cmark_html_render_cr(html);
|
||||
cmark_strbuf_puts(html, "<hr");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_puts(html, " />\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
parent = cmark_node_parent(node);
|
||||
grandparent = cmark_node_parent(parent);
|
||||
if (grandparent != NULL && grandparent->type == CMARK_NODE_LIST) {
|
||||
tight = grandparent->as.list.tight;
|
||||
} else {
|
||||
tight = false;
|
||||
}
|
||||
if (!tight) {
|
||||
if (entering) {
|
||||
cmark_html_render_cr(html);
|
||||
cmark_strbuf_puts(html, "<p");
|
||||
cmark_html_render_sourcepos(node, html, options);
|
||||
cmark_strbuf_putc(html, '>');
|
||||
} else {
|
||||
if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) {
|
||||
cmark_strbuf_putc(html, ' ');
|
||||
S_put_footnote_backref(renderer, html, parent);
|
||||
}
|
||||
cmark_strbuf_puts(html, "</p>\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
escape_html(html, node->as.literal.data, node->as.literal.len);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
cmark_strbuf_puts(html, "<br />\n");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
cmark_strbuf_puts(html, "<br />\n");
|
||||
} else if (options & CMARK_OPT_NOBREAKS) {
|
||||
cmark_strbuf_putc(html, ' ');
|
||||
} else {
|
||||
cmark_strbuf_putc(html, '\n');
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
cmark_strbuf_puts(html, "<code>");
|
||||
escape_html(html, node->as.literal.data, node->as.literal.len);
|
||||
cmark_strbuf_puts(html, "</code>");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
if (!(options & CMARK_OPT_UNSAFE)) {
|
||||
cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
|
||||
} else {
|
||||
filtered = false;
|
||||
for (it = renderer->filter_extensions; it; it = it->next) {
|
||||
ext = (cmark_syntax_extension *) it->data;
|
||||
if (!ext->html_filter_func(ext, node->as.literal.data, node->as.literal.len)) {
|
||||
filtered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!filtered) {
|
||||
cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "<");
|
||||
cmark_strbuf_put(html, node->as.literal.data + 1, node->as.literal.len - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
if (entering) {
|
||||
cmark_strbuf_put(html, node->as.custom.on_enter.data,
|
||||
node->as.custom.on_enter.len);
|
||||
} else {
|
||||
cmark_strbuf_put(html, node->as.custom.on_exit.data,
|
||||
node->as.custom.on_exit.len);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<strong>");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</strong>");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<em>");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</em>");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<a href=\"");
|
||||
if ((options & CMARK_OPT_UNSAFE) ||
|
||||
!(scan_dangerous_url(&node->as.link.url, 0))) {
|
||||
houdini_escape_href(html, node->as.link.url.data,
|
||||
node->as.link.url.len);
|
||||
}
|
||||
if (node->as.link.title.len) {
|
||||
cmark_strbuf_puts(html, "\" title=\"");
|
||||
escape_html(html, node->as.link.title.data, node->as.link.title.len);
|
||||
}
|
||||
cmark_strbuf_puts(html, "\">");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</a>");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<img src=\"");
|
||||
if ((options & CMARK_OPT_UNSAFE) ||
|
||||
!(scan_dangerous_url(&node->as.link.url, 0))) {
|
||||
houdini_escape_href(html, node->as.link.url.data,
|
||||
node->as.link.url.len);
|
||||
}
|
||||
cmark_strbuf_puts(html, "\" alt=\"");
|
||||
renderer->plain = node;
|
||||
} else {
|
||||
if (node->as.link.title.len) {
|
||||
cmark_strbuf_puts(html, "\" title=\"");
|
||||
escape_html(html, node->as.link.title.data, node->as.link.title.len);
|
||||
}
|
||||
|
||||
cmark_strbuf_puts(html, "\" />");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ATTRIBUTE:
|
||||
// TODO: Output span, attributes potentially controlling class/id here. For now just output the main string.
|
||||
/*
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<span __attributes=\"");
|
||||
cmark_strbuf_put(html, node->as.attribute.attributes.data, node->as.attribute.attributes.len);
|
||||
cmark_strbuf_puts(html, "\">");
|
||||
} else {
|
||||
cmark_strbuf_puts(html, "</span>");
|
||||
}
|
||||
*/
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
||||
if (entering) {
|
||||
if (renderer->footnote_ix == 0) {
|
||||
cmark_strbuf_puts(html, "<section class=\"footnotes\" data-footnotes>\n<ol>\n");
|
||||
}
|
||||
++renderer->footnote_ix;
|
||||
|
||||
cmark_strbuf_puts(html, "<li id=\"fn-");
|
||||
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
||||
cmark_strbuf_puts(html, "\">\n");
|
||||
} else {
|
||||
if (S_put_footnote_backref(renderer, html, node)) {
|
||||
cmark_strbuf_putc(html, '\n');
|
||||
}
|
||||
cmark_strbuf_puts(html, "</li>\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
||||
if (entering) {
|
||||
cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn-");
|
||||
houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
||||
cmark_strbuf_puts(html, "\" id=\"fnref-");
|
||||
houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
||||
|
||||
if (node->footnote.ref_ix > 1) {
|
||||
char n[32];
|
||||
snprintf(n, sizeof(n), "%d", node->footnote.ref_ix);
|
||||
cmark_strbuf_puts(html, "-");
|
||||
cmark_strbuf_puts(html, n);
|
||||
}
|
||||
|
||||
cmark_strbuf_puts(html, "\" data-footnote-ref>");
|
||||
houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
|
||||
cmark_strbuf_puts(html, "</a></sup>");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_html(cmark_node *root, int options, cmark_llist *extensions) {
|
||||
return cmark_render_html_with_mem(root, options, extensions, cmark_node_mem(root));
|
||||
}
|
||||
|
||||
char *cmark_render_html_with_mem(cmark_node *root, int options, cmark_llist *extensions, cmark_mem *mem) {
|
||||
char *result;
|
||||
cmark_strbuf html = CMARK_BUF_INIT(mem);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur;
|
||||
cmark_html_renderer renderer = {&html, NULL, NULL, 0, 0, NULL};
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
for (; extensions; extensions = extensions->next)
|
||||
if (((cmark_syntax_extension *) extensions->data)->html_filter_func)
|
||||
renderer.filter_extensions = cmark_llist_append(
|
||||
mem,
|
||||
renderer.filter_extensions,
|
||||
(cmark_syntax_extension *) extensions->data);
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
S_render_node(&renderer, cur, ev_type, options);
|
||||
}
|
||||
|
||||
if (renderer.footnote_ix) {
|
||||
cmark_strbuf_puts(&html, "</ol>\n</section>\n");
|
||||
}
|
||||
|
||||
result = (char *)cmark_strbuf_detach(&html);
|
||||
|
||||
cmark_llist_free(mem, renderer.filter_extensions);
|
||||
|
||||
cmark_iter_free(iter);
|
||||
return result;
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
#ifndef CMARK_BUFFER_H
|
||||
#define CMARK_BUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include "cmark-gfm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
cmark_mem *mem;
|
||||
unsigned char *ptr;
|
||||
bufsize_t asize, size;
|
||||
} cmark_strbuf;
|
||||
|
||||
extern unsigned char cmark_strbuf__initbuf[];
|
||||
|
||||
#define CMARK_BUF_INIT(mem) \
|
||||
{ mem, cmark_strbuf__initbuf, 0, 0 }
|
||||
|
||||
/**
|
||||
* Initialize a cmark_strbuf structure.
|
||||
*
|
||||
* For the cases where CMARK_BUF_INIT cannot be used to do static
|
||||
* initialization.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf,
|
||||
bufsize_t initial_size);
|
||||
|
||||
/**
|
||||
* Grow the buffer to hold at least `target_size` bytes.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_free(cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_swap(cmark_strbuf *buf_a, cmark_strbuf *buf_b);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bufsize_t cmark_strbuf_len(const cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
unsigned char *cmark_strbuf_detach(cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize,
|
||||
const cmark_strbuf *buf);
|
||||
|
||||
static inline const char *cmark_strbuf_cstr(const cmark_strbuf *buf) {
|
||||
return (char *)buf->ptr;
|
||||
}
|
||||
|
||||
#define cmark_strbuf_at(buf, n) ((buf)->ptr[n])
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_sets(cmark_strbuf *buf, const char *string);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_putc(cmark_strbuf *buf, int c);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data,
|
||||
bufsize_t len);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_puts(cmark_strbuf *buf, const char *string);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_clear(cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_rtrim(cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_trim(cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_normalize_whitespace(cmark_strbuf *s);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_strbuf_unescape(cmark_strbuf *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,134 +0,0 @@
|
||||
#ifndef CMARK_CHUNK_H
|
||||
#define CMARK_CHUNK_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "cmark-gfm.h"
|
||||
#include "buffer.h"
|
||||
#include "cmark_ctype.h"
|
||||
|
||||
#define CMARK_CHUNK_EMPTY \
|
||||
{ NULL, 0, 0 }
|
||||
|
||||
typedef struct cmark_chunk {
|
||||
unsigned char *data;
|
||||
bufsize_t len;
|
||||
bufsize_t alloc; // also implies a NULL-terminated string
|
||||
} cmark_chunk;
|
||||
|
||||
static inline void cmark_chunk_free(cmark_mem *mem, cmark_chunk *c) {
|
||||
if (c->alloc)
|
||||
mem->free(c->data);
|
||||
|
||||
c->data = NULL;
|
||||
c->alloc = 0;
|
||||
c->len = 0;
|
||||
}
|
||||
|
||||
static inline void cmark_chunk_ltrim(cmark_chunk *c) {
|
||||
assert(!c->alloc);
|
||||
|
||||
while (c->len && cmark_isspace(c->data[0])) {
|
||||
c->data++;
|
||||
c->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cmark_chunk_rtrim(cmark_chunk *c) {
|
||||
assert(!c->alloc);
|
||||
|
||||
while (c->len > 0) {
|
||||
if (!cmark_isspace(c->data[c->len - 1]))
|
||||
break;
|
||||
|
||||
c->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cmark_chunk_trim(cmark_chunk *c) {
|
||||
cmark_chunk_ltrim(c);
|
||||
cmark_chunk_rtrim(c);
|
||||
}
|
||||
|
||||
static inline bufsize_t cmark_chunk_strchr(cmark_chunk *ch, int c,
|
||||
bufsize_t offset) {
|
||||
const unsigned char *p =
|
||||
(unsigned char *)memchr(ch->data + offset, c, ch->len - offset);
|
||||
return p ? (bufsize_t)(p - ch->data) : ch->len;
|
||||
}
|
||||
|
||||
static inline const char *cmark_chunk_to_cstr(cmark_mem *mem, cmark_chunk *c) {
|
||||
unsigned char *str;
|
||||
|
||||
if (c->alloc) {
|
||||
return (char *)c->data;
|
||||
}
|
||||
str = (unsigned char *)mem->calloc(c->len + 1, 1);
|
||||
if (c->len > 0) {
|
||||
memcpy(str, c->data, c->len);
|
||||
}
|
||||
str[c->len] = 0;
|
||||
c->data = str;
|
||||
c->alloc = 1;
|
||||
|
||||
return (char *)str;
|
||||
}
|
||||
|
||||
static inline void cmark_chunk_set_cstr(cmark_mem *mem, cmark_chunk *c,
|
||||
const char *str) {
|
||||
unsigned char *old = c->alloc ? c->data : NULL;
|
||||
if (str == NULL) {
|
||||
c->len = 0;
|
||||
c->data = NULL;
|
||||
c->alloc = 0;
|
||||
} else {
|
||||
c->len = (bufsize_t)strlen(str);
|
||||
c->data = (unsigned char *)mem->calloc(c->len + 1, 1);
|
||||
c->alloc = 1;
|
||||
memcpy(c->data, str, c->len + 1);
|
||||
}
|
||||
if (old != NULL) {
|
||||
mem->free(old);
|
||||
}
|
||||
}
|
||||
|
||||
static inline cmark_chunk cmark_chunk_literal(const char *data) {
|
||||
bufsize_t len = data ? (bufsize_t)strlen(data) : 0;
|
||||
cmark_chunk c = {(unsigned char *)data, len, 0};
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline cmark_chunk cmark_chunk_dup(const cmark_chunk *ch, bufsize_t pos,
|
||||
bufsize_t len) {
|
||||
cmark_chunk c = {ch->data + pos, len, 0};
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline cmark_chunk cmark_chunk_buf_detach(cmark_strbuf *buf) {
|
||||
cmark_chunk c;
|
||||
|
||||
c.len = buf->size;
|
||||
c.data = cmark_strbuf_detach(buf);
|
||||
c.alloc = 1;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* trim_new variants are to be used when the source chunk may or may not be
|
||||
* allocated; forces a newly allocated chunk. */
|
||||
static inline cmark_chunk cmark_chunk_ltrim_new(cmark_mem *mem, cmark_chunk *c) {
|
||||
cmark_chunk r = cmark_chunk_dup(c, 0, c->len);
|
||||
cmark_chunk_ltrim(&r);
|
||||
cmark_chunk_to_cstr(mem, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline cmark_chunk cmark_chunk_rtrim_new(cmark_mem *mem, cmark_chunk *c) {
|
||||
cmark_chunk r = cmark_chunk_dup(c, 0, c->len);
|
||||
cmark_chunk_rtrim(&r);
|
||||
cmark_chunk_to_cstr(mem, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,739 +0,0 @@
|
||||
#ifndef CMARK_GFM_EXTENSION_API_H
|
||||
#define CMARK_GFM_EXTENSION_API_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct cmark_renderer;
|
||||
struct cmark_html_renderer;
|
||||
struct cmark_chunk;
|
||||
|
||||
/**
|
||||
* ## Extension Support
|
||||
*
|
||||
* While the "core" of libcmark is strictly compliant with the
|
||||
* specification, an API is provided for extension writers to
|
||||
* hook into the parsing process.
|
||||
*
|
||||
* It should be noted that the cmark_node API already offers
|
||||
* room for customization, with methods offered to traverse and
|
||||
* modify the AST, and even define custom blocks.
|
||||
* When the desired customization is achievable in an error-proof
|
||||
* way using that API, it should be the preferred method.
|
||||
*
|
||||
* The following API requires a more in-depth understanding
|
||||
* of libcmark's parsing strategy, which is exposed
|
||||
* [here](http://spec.commonmark.org/0.24/#appendix-a-parsing-strategy).
|
||||
*
|
||||
* It should be used when "a posteriori" modification of the AST
|
||||
* proves to be too difficult / impossible to implement correctly.
|
||||
*
|
||||
* It can also serve as an intermediary step before extending
|
||||
* the specification, as an extension implemented using this API
|
||||
* will be trivially integrated in the core if it proves to be
|
||||
* desirable.
|
||||
*/
|
||||
|
||||
typedef struct cmark_plugin cmark_plugin;
|
||||
|
||||
/** A syntax extension that can be attached to a cmark_parser
|
||||
* with cmark_parser_attach_syntax_extension().
|
||||
*
|
||||
* Extension writers should assign functions matching
|
||||
* the signature of the following 'virtual methods' to
|
||||
* implement new functionality.
|
||||
*
|
||||
* Their calling order and expected behaviour match the procedure outlined
|
||||
* at <http://spec.commonmark.org/0.24/#phase-1-block-structure>:
|
||||
*
|
||||
* During step 1, cmark will call the function provided through
|
||||
* 'cmark_syntax_extension_set_match_block_func' when it
|
||||
* iterates over an open block created by this extension,
|
||||
* to determine whether it could contain the new line.
|
||||
* If no function was provided, cmark will close the block.
|
||||
*
|
||||
* During step 2, if and only if the new line doesn't match any
|
||||
* of the standard syntax rules, cmark will call the function
|
||||
* provided through 'cmark_syntax_extension_set_open_block_func'
|
||||
* to let the extension determine whether that new line matches
|
||||
* one of its syntax rules.
|
||||
* It is the responsibility of the parser to create and add the
|
||||
* new block with cmark_parser_make_block and cmark_parser_add_child.
|
||||
* If no function was provided is NULL, the extension will have
|
||||
* no effect at all on the final block structure of the AST.
|
||||
*
|
||||
* #### Inline parsing phase hooks
|
||||
*
|
||||
* For each character provided by the extension through
|
||||
* 'cmark_syntax_extension_set_special_inline_chars',
|
||||
* the function provided by the extension through
|
||||
* 'cmark_syntax_extension_set_match_inline_func'
|
||||
* will get called, it is the responsibility of the extension
|
||||
* to scan the characters located at the current inline parsing offset
|
||||
* with the cmark_inline_parser API.
|
||||
*
|
||||
* Depending on the type of the extension, it can either:
|
||||
*
|
||||
* * Scan forward, determine that the syntax matches and return
|
||||
* a newly-created inline node with the appropriate type.
|
||||
* This is the technique that would be used if inline code
|
||||
* (with backticks) was implemented as an extension.
|
||||
* * Scan only the character(s) that its syntax rules require
|
||||
* for opening and closing nodes, push a delimiter on the
|
||||
* delimiter stack, and return a simple text node with its
|
||||
* contents set to the character(s) consumed.
|
||||
* This is the technique that would be used if emphasis
|
||||
* inlines were implemented as an extension.
|
||||
*
|
||||
* When an extension has pushed delimiters on the stack,
|
||||
* the function provided through
|
||||
* 'cmark_syntax_extension_set_inline_from_delim_func'
|
||||
* will get called in a latter phase,
|
||||
* when the inline parser has matched opener and closer delimiters
|
||||
* created by the extension together.
|
||||
*
|
||||
* It is then the responsibility of the extension to modify
|
||||
* and populate the opener inline text node, and to remove
|
||||
* the necessary delimiters from the delimiter stack.
|
||||
*
|
||||
* Finally, the extension should return NULL if its scan didn't
|
||||
* match its syntax rules.
|
||||
*
|
||||
* The extension can store whatever private data it might need
|
||||
* with 'cmark_syntax_extension_set_private',
|
||||
* and optionally define a free function for this data.
|
||||
*/
|
||||
typedef struct subject cmark_inline_parser;
|
||||
|
||||
/** Exposed raw for now */
|
||||
|
||||
typedef struct delimiter {
|
||||
struct delimiter *previous;
|
||||
struct delimiter *next;
|
||||
cmark_node *inl_text;
|
||||
bufsize_t position;
|
||||
bufsize_t length;
|
||||
unsigned char delim_char;
|
||||
int can_open;
|
||||
int can_close;
|
||||
} delimiter;
|
||||
|
||||
/**
|
||||
* ### Plugin API.
|
||||
*
|
||||
* Extensions should be distributed as dynamic libraries,
|
||||
* with a single exported function named after the distributed
|
||||
* filename.
|
||||
*
|
||||
* When discovering extensions (see cmark_init), cmark will
|
||||
* try to load a symbol named "init_{{filename}}" in all the
|
||||
* dynamic libraries it encounters.
|
||||
*
|
||||
* For example, given a dynamic library named myextension.so
|
||||
* (or myextension.dll), cmark will try to load the symbol
|
||||
* named "init_myextension". This means that the filename
|
||||
* must lend itself to forming a valid C identifier, with
|
||||
* the notable exception of dashes, which will be translated
|
||||
* to underscores, which means cmark will look for a function
|
||||
* named "init_my_extension" if it encounters a dynamic library
|
||||
* named "my-extension.so".
|
||||
*
|
||||
* See the 'cmark_plugin_init_func' typedef for the exact prototype
|
||||
* this function should follow.
|
||||
*
|
||||
* For now the extensibility of cmark is not complete, as
|
||||
* it only offers API to hook into the block parsing phase
|
||||
* (<http://spec.commonmark.org/0.24/#phase-1-block-structure>).
|
||||
*
|
||||
* See 'cmark_plugin_register_syntax_extension' for more information.
|
||||
*/
|
||||
|
||||
/** The prototype plugins' init function should follow.
|
||||
*/
|
||||
typedef int (*cmark_plugin_init_func)(cmark_plugin *plugin);
|
||||
|
||||
/** Register a syntax 'extension' with the 'plugin', it will be made
|
||||
* available as an extension and, if attached to a cmark_parser
|
||||
* with 'cmark_parser_attach_syntax_extension', it will contribute
|
||||
* to the block parsing process.
|
||||
*
|
||||
* See the documentation for 'cmark_syntax_extension' for information
|
||||
* on how to implement one.
|
||||
*
|
||||
* This function will typically be called from the init function
|
||||
* of external modules.
|
||||
*
|
||||
* This takes ownership of 'extension', one should not call
|
||||
* 'cmark_syntax_extension_free' on a registered extension.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_plugin_register_syntax_extension(cmark_plugin *plugin,
|
||||
cmark_syntax_extension *extension);
|
||||
|
||||
/** This will search for the syntax extension named 'name' among the
|
||||
* registered syntax extensions.
|
||||
*
|
||||
* It can then be attached to a cmark_parser
|
||||
* with the cmark_parser_attach_syntax_extension method.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_syntax_extension *cmark_find_syntax_extension(const char *name);
|
||||
|
||||
/** Should create and add a new open block to 'parent_container' if
|
||||
* 'input' matches a syntax rule for that block type. It is allowed
|
||||
* to modify the type of 'parent_container'.
|
||||
*
|
||||
* Should return the newly created block if there is one, or
|
||||
* 'parent_container' if its type was modified, or NULL.
|
||||
*/
|
||||
typedef cmark_node * (*cmark_open_block_func) (cmark_syntax_extension *extension,
|
||||
int indented,
|
||||
cmark_parser *parser,
|
||||
cmark_node *parent_container,
|
||||
unsigned char *input,
|
||||
int len);
|
||||
|
||||
typedef cmark_node *(*cmark_match_inline_func)(cmark_syntax_extension *extension,
|
||||
cmark_parser *parser,
|
||||
cmark_node *parent,
|
||||
unsigned char character,
|
||||
cmark_inline_parser *inline_parser);
|
||||
|
||||
typedef delimiter *(*cmark_inline_from_delim_func)(cmark_syntax_extension *extension,
|
||||
cmark_parser *parser,
|
||||
cmark_inline_parser *inline_parser,
|
||||
delimiter *opener,
|
||||
delimiter *closer);
|
||||
|
||||
/** Should return 'true' if 'input' can be contained in 'container',
|
||||
* 'false' otherwise.
|
||||
*/
|
||||
typedef int (*cmark_match_block_func) (cmark_syntax_extension *extension,
|
||||
cmark_parser *parser,
|
||||
unsigned char *input,
|
||||
int len,
|
||||
cmark_node *container);
|
||||
|
||||
typedef const char *(*cmark_get_type_string_func) (cmark_syntax_extension *extension,
|
||||
cmark_node *node);
|
||||
|
||||
typedef int (*cmark_can_contain_func) (cmark_syntax_extension *extension,
|
||||
cmark_node *node,
|
||||
cmark_node_type child);
|
||||
|
||||
typedef int (*cmark_contains_inlines_func) (cmark_syntax_extension *extension,
|
||||
cmark_node *node);
|
||||
|
||||
typedef void (*cmark_common_render_func) (cmark_syntax_extension *extension,
|
||||
struct cmark_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type,
|
||||
int options);
|
||||
|
||||
typedef int (*cmark_commonmark_escape_func) (cmark_syntax_extension *extension,
|
||||
cmark_node *node,
|
||||
int c);
|
||||
|
||||
typedef const char* (*cmark_xml_attr_func) (cmark_syntax_extension *extension,
|
||||
cmark_node *node);
|
||||
|
||||
typedef void (*cmark_html_render_func) (cmark_syntax_extension *extension,
|
||||
struct cmark_html_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type,
|
||||
int options);
|
||||
|
||||
typedef int (*cmark_html_filter_func) (cmark_syntax_extension *extension,
|
||||
const unsigned char *tag,
|
||||
size_t tag_len);
|
||||
|
||||
typedef cmark_node *(*cmark_postprocess_func) (cmark_syntax_extension *extension,
|
||||
cmark_parser *parser,
|
||||
cmark_node *root);
|
||||
|
||||
typedef int (*cmark_ispunct_func) (char c);
|
||||
|
||||
typedef void (*cmark_opaque_alloc_func) (cmark_syntax_extension *extension,
|
||||
cmark_mem *mem,
|
||||
cmark_node *node);
|
||||
|
||||
typedef void (*cmark_opaque_free_func) (cmark_syntax_extension *extension,
|
||||
cmark_mem *mem,
|
||||
cmark_node *node);
|
||||
|
||||
/** Free a cmark_syntax_extension.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_free (cmark_mem *mem, cmark_syntax_extension *extension);
|
||||
|
||||
/** Return a newly-constructed cmark_syntax_extension, named 'name'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_syntax_extension *cmark_syntax_extension_new (const char *name);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node_type cmark_syntax_extension_add_node(int is_inline);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_emphasis(cmark_syntax_extension *extension, int emphasis);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_open_block_func(cmark_syntax_extension *extension,
|
||||
cmark_open_block_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_match_block_func(cmark_syntax_extension *extension,
|
||||
cmark_match_block_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_match_inline_func(cmark_syntax_extension *extension,
|
||||
cmark_match_inline_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_inline_from_delim_func(cmark_syntax_extension *extension,
|
||||
cmark_inline_from_delim_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_special_inline_chars(cmark_syntax_extension *extension,
|
||||
cmark_llist *special_chars);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_get_type_string_func(cmark_syntax_extension *extension,
|
||||
cmark_get_type_string_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_can_contain_func(cmark_syntax_extension *extension,
|
||||
cmark_can_contain_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_contains_inlines_func(cmark_syntax_extension *extension,
|
||||
cmark_contains_inlines_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_commonmark_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_plaintext_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_xml_attr_func(cmark_syntax_extension *extension,
|
||||
cmark_xml_attr_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_html_render_func(cmark_syntax_extension *extension,
|
||||
cmark_html_render_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_html_filter_func(cmark_syntax_extension *extension,
|
||||
cmark_html_filter_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_commonmark_escape_func(cmark_syntax_extension *extension,
|
||||
cmark_commonmark_escape_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_private(cmark_syntax_extension *extension,
|
||||
void *priv,
|
||||
cmark_free_func free_func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void *cmark_syntax_extension_get_private(cmark_syntax_extension *extension);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension,
|
||||
cmark_postprocess_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_opaque_alloc_func(cmark_syntax_extension *extension,
|
||||
cmark_opaque_alloc_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension,
|
||||
cmark_opaque_free_func func);
|
||||
|
||||
/** See the documentation for 'cmark_syntax_extension'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parser_set_backslash_ispunct_func(cmark_parser *parser,
|
||||
cmark_ispunct_func func);
|
||||
|
||||
/** Return the index of the line currently being parsed, starting with 1.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_line_number(cmark_parser *parser);
|
||||
|
||||
/** Return the offset in bytes in the line being processed.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ### foo
|
||||
*
|
||||
* Here, offset will first be 0, then 5 (the index of the 'f' character).
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_offset(cmark_parser *parser);
|
||||
|
||||
/**
|
||||
* Return the offset in 'columns' in the line being processed.
|
||||
*
|
||||
* This value may differ from the value returned by
|
||||
* cmark_parser_get_offset() in that it accounts for tabs,
|
||||
* and as such should not be used as an index in the current line's
|
||||
* buffer.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* cmark_parser_advance_offset() can be called to advance the
|
||||
* offset by a number of columns, instead of a number of bytes.
|
||||
*
|
||||
* In that case, if offset falls "in the middle" of a tab
|
||||
* character, 'column' and offset will differ.
|
||||
*
|
||||
* ```
|
||||
* foo \t bar
|
||||
* ^ ^^
|
||||
* offset (0) 20
|
||||
* ```
|
||||
*
|
||||
* If cmark_parser_advance_offset is called here with 'columns'
|
||||
* set to 'true' and 'offset' set to 22, cmark_parser_get_offset()
|
||||
* will return 20, whereas cmark_parser_get_column() will return
|
||||
* 22.
|
||||
*
|
||||
* Additionally, as tabs expand to the next multiple of 4 column,
|
||||
* cmark_parser_has_partially_consumed_tab() will now return
|
||||
* 'true'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_column(cmark_parser *parser);
|
||||
|
||||
/** Return the absolute index in bytes of the first nonspace
|
||||
* character coming after the offset as returned by
|
||||
* cmark_parser_get_offset() in the line currently being processed.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* foo bar baz \n
|
||||
* ^ ^ ^
|
||||
* 0 offset (16) first_nonspace (28)
|
||||
* ```
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_first_nonspace(cmark_parser *parser);
|
||||
|
||||
/** Return the absolute index of the first nonspace column coming after 'offset'
|
||||
* in the line currently being processed, counting tabs as multiple
|
||||
* columns as appropriate.
|
||||
*
|
||||
* See the documentation for cmark_parser_get_first_nonspace() and
|
||||
* cmark_parser_get_column() for more information.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_first_nonspace_column(cmark_parser *parser);
|
||||
|
||||
/** Return the difference between the values returned by
|
||||
* cmark_parser_get_first_nonspace_column() and
|
||||
* cmark_parser_get_column().
|
||||
*
|
||||
* This is not a byte offset, as it can count one tab as multiple
|
||||
* characters.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_indent(cmark_parser *parser);
|
||||
|
||||
/** Return 'true' if the line currently being processed has been entirely
|
||||
* consumed, 'false' otherwise.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* foo bar baz \n
|
||||
* ^
|
||||
* offset
|
||||
* ```
|
||||
*
|
||||
* This function will return 'false' here.
|
||||
*
|
||||
* ```
|
||||
* foo bar baz \n
|
||||
* ^
|
||||
* offset
|
||||
* ```
|
||||
* This function will still return 'false'.
|
||||
*
|
||||
* ```
|
||||
* foo bar baz \n
|
||||
* ^
|
||||
* offset
|
||||
* ```
|
||||
*
|
||||
* At this point, this function will now return 'true'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_is_blank(cmark_parser *parser);
|
||||
|
||||
/** Return 'true' if the value returned by cmark_parser_get_offset()
|
||||
* is 'inside' an expanded tab.
|
||||
*
|
||||
* See the documentation for cmark_parser_get_column() for more
|
||||
* information.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_has_partially_consumed_tab(cmark_parser *parser);
|
||||
|
||||
/** Return the length in bytes of the previously processed line, excluding potential
|
||||
* newline (\n) and carriage return (\r) trailing characters.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_get_last_line_length(cmark_parser *parser);
|
||||
|
||||
/** Add a child to 'parent' during the parsing process.
|
||||
*
|
||||
* If 'parent' isn't the kind of node that can accept this child,
|
||||
* this function will back up till it hits a node that can, closing
|
||||
* blocks as appropriate.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node*cmark_parser_add_child(cmark_parser *parser,
|
||||
cmark_node *parent,
|
||||
cmark_node_type block_type,
|
||||
int start_column);
|
||||
|
||||
/** Advance the 'offset' of the parser in the current line.
|
||||
*
|
||||
* See the documentation of cmark_parser_get_offset() and
|
||||
* cmark_parser_get_column() for more information.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parser_advance_offset(cmark_parser *parser,
|
||||
const char *input,
|
||||
int count,
|
||||
int columns);
|
||||
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parser_feed_reentrant(cmark_parser *parser, const char *buffer, size_t len);
|
||||
|
||||
/** Attach the syntax 'extension' to the 'parser', to provide extra syntax
|
||||
* rules.
|
||||
* See the documentation for cmark_syntax_extension for more information.
|
||||
*
|
||||
* Returns 'true' if the 'extension' was successfully attached,
|
||||
* 'false' otherwise.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_parser_attach_syntax_extension(cmark_parser *parser, cmark_syntax_extension *extension);
|
||||
|
||||
/** Change the type of 'node'.
|
||||
*
|
||||
* Return 0 if the type could be changed, 1 otherwise.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_type(cmark_node *node, cmark_node_type type);
|
||||
|
||||
/** Return the string content for all types of 'node'.
|
||||
* The pointer stays valid as long as 'node' isn't freed.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_string_content(cmark_node *node);
|
||||
|
||||
/** Set the string 'content' for all types of 'node'.
|
||||
* Copies 'content'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_string_content(cmark_node *node, const char *content);
|
||||
|
||||
/** Get the syntax extension responsible for the creation of 'node'.
|
||||
* Return NULL if 'node' was created because it matched standard syntax rules.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_syntax_extension *cmark_node_get_syntax_extension(cmark_node *node);
|
||||
|
||||
/** Set the syntax extension responsible for creating 'node'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_syntax_extension(cmark_node *node,
|
||||
cmark_syntax_extension *extension);
|
||||
|
||||
/**
|
||||
* ## Inline syntax extension helpers
|
||||
*
|
||||
* The inline parsing process is described in detail at
|
||||
* <http://spec.commonmark.org/0.24/#phase-2-inline-structure>
|
||||
*/
|
||||
|
||||
/** Should return 'true' if the predicate matches 'c', 'false' otherwise
|
||||
*/
|
||||
typedef int (*cmark_inline_predicate)(int c);
|
||||
|
||||
/** Advance the current inline parsing offset */
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_inline_parser_advance_offset(cmark_inline_parser *parser);
|
||||
|
||||
/** Get the current inline parsing offset */
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_get_offset(cmark_inline_parser *parser);
|
||||
|
||||
/** Set the offset in bytes in the chunk being processed by the given inline parser.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_inline_parser_set_offset(cmark_inline_parser *parser, int offset);
|
||||
|
||||
/** Gets the cmark_chunk being operated on by the given inline parser.
|
||||
* Use cmark_inline_parser_get_offset to get our current position in the chunk.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
struct cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser);
|
||||
|
||||
/** Returns 1 if the inline parser is currently in a bracket; pass 2 for attribute,
|
||||
* 1 for 'image' if you want to know about an image-type bracket, 0 for link-type. */
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int type);
|
||||
|
||||
/** Remove the last n characters from the last child of the given node.
|
||||
* This only works where all n characters are in the single last child, and the last
|
||||
* child is CMARK_NODE_TEXT.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_node_unput(cmark_node *node, int n);
|
||||
|
||||
|
||||
/** Get the character located at the current inline parsing offset
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
unsigned char cmark_inline_parser_peek_char(cmark_inline_parser *parser);
|
||||
|
||||
/** Get the character located 'pos' bytes in the current line.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
unsigned char cmark_inline_parser_peek_at(cmark_inline_parser *parser, int pos);
|
||||
|
||||
/** Whether the inline parser has reached the end of the current line
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_is_eof(cmark_inline_parser *parser);
|
||||
|
||||
/** Get the characters located after the current inline parsing offset
|
||||
* while 'pred' matches. Free after usage.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_inline_parser_take_while(cmark_inline_parser *parser, cmark_inline_predicate pred);
|
||||
|
||||
/** Push a delimiter on the delimiter stack.
|
||||
* See <<http://spec.commonmark.org/0.24/#phase-2-inline-structure> for
|
||||
* more information on the parameters
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_inline_parser_push_delimiter(cmark_inline_parser *parser,
|
||||
unsigned char c,
|
||||
int can_open,
|
||||
int can_close,
|
||||
cmark_node *inl_text);
|
||||
|
||||
/** Remove 'delim' from the delimiter stack
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_inline_parser_remove_delimiter(cmark_inline_parser *parser, delimiter *delim);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
delimiter *cmark_inline_parser_get_last_delimiter(cmark_inline_parser *parser);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_get_line(cmark_inline_parser *parser);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_get_column(cmark_inline_parser *parser);
|
||||
|
||||
/** Convenience function to scan a given delimiter.
|
||||
*
|
||||
* 'left_flanking' and 'right_flanking' will be set to true if they
|
||||
* respectively precede and follow a non-space, non-punctuation
|
||||
* character.
|
||||
*
|
||||
* Additionally, 'punct_before' and 'punct_after' will respectively be set
|
||||
* if the preceding or following character is a punctuation character.
|
||||
*
|
||||
* Note that 'left_flanking' and 'right_flanking' can both be 'true'.
|
||||
*
|
||||
* Returns the number of delimiters encountered, in the limit
|
||||
* of 'max_delims', and advances the inline parsing offset.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_inline_parser_scan_delimiters(cmark_inline_parser *parser,
|
||||
int max_delims,
|
||||
unsigned char c,
|
||||
int *left_flanking,
|
||||
int *right_flanking,
|
||||
int *punct_before,
|
||||
int *punct_after);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_manage_extensions_special_characters(cmark_parser *parser, int add);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_llist *cmark_parser_get_syntax_extensions(cmark_parser *parser);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_arena_push(void);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_arena_pop(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,876 +0,0 @@
|
||||
#ifndef CMARK_GFM_H
|
||||
#define CMARK_GFM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "export.h"
|
||||
#include "cmark-gfm_version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** # NAME
|
||||
*
|
||||
* **cmark-gfm** - CommonMark parsing, manipulating, and rendering
|
||||
*/
|
||||
|
||||
/** # DESCRIPTION
|
||||
*
|
||||
* ## Simple Interface
|
||||
*/
|
||||
|
||||
/** Convert 'text' (assumed to be a UTF-8 encoded string with length
|
||||
* 'len') from CommonMark Markdown to HTML, returning a null-terminated,
|
||||
* UTF-8-encoded string. It is the caller's responsibility
|
||||
* to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_markdown_to_html(const char *text, size_t len, int options);
|
||||
|
||||
/** ## Node Structure
|
||||
*/
|
||||
|
||||
#define CMARK_NODE_TYPE_PRESENT (0x8000)
|
||||
#define CMARK_NODE_TYPE_BLOCK (CMARK_NODE_TYPE_PRESENT | 0x0000)
|
||||
#define CMARK_NODE_TYPE_INLINE (CMARK_NODE_TYPE_PRESENT | 0x4000)
|
||||
#define CMARK_NODE_TYPE_MASK (0xc000)
|
||||
#define CMARK_NODE_VALUE_MASK (0x3fff)
|
||||
|
||||
typedef enum {
|
||||
/* Error status */
|
||||
CMARK_NODE_NONE = 0x0000,
|
||||
|
||||
/* Block */
|
||||
CMARK_NODE_DOCUMENT = CMARK_NODE_TYPE_BLOCK | 0x0001,
|
||||
CMARK_NODE_BLOCK_QUOTE = CMARK_NODE_TYPE_BLOCK | 0x0002,
|
||||
CMARK_NODE_LIST = CMARK_NODE_TYPE_BLOCK | 0x0003,
|
||||
CMARK_NODE_ITEM = CMARK_NODE_TYPE_BLOCK | 0x0004,
|
||||
CMARK_NODE_CODE_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0005,
|
||||
CMARK_NODE_HTML_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0006,
|
||||
CMARK_NODE_CUSTOM_BLOCK = CMARK_NODE_TYPE_BLOCK | 0x0007,
|
||||
CMARK_NODE_PARAGRAPH = CMARK_NODE_TYPE_BLOCK | 0x0008,
|
||||
CMARK_NODE_HEADING = CMARK_NODE_TYPE_BLOCK | 0x0009,
|
||||
CMARK_NODE_THEMATIC_BREAK = CMARK_NODE_TYPE_BLOCK | 0x000a,
|
||||
CMARK_NODE_FOOTNOTE_DEFINITION = CMARK_NODE_TYPE_BLOCK | 0x000b,
|
||||
|
||||
/* Inline */
|
||||
CMARK_NODE_TEXT = CMARK_NODE_TYPE_INLINE | 0x0001,
|
||||
CMARK_NODE_SOFTBREAK = CMARK_NODE_TYPE_INLINE | 0x0002,
|
||||
CMARK_NODE_LINEBREAK = CMARK_NODE_TYPE_INLINE | 0x0003,
|
||||
CMARK_NODE_CODE = CMARK_NODE_TYPE_INLINE | 0x0004,
|
||||
CMARK_NODE_HTML_INLINE = CMARK_NODE_TYPE_INLINE | 0x0005,
|
||||
CMARK_NODE_CUSTOM_INLINE = CMARK_NODE_TYPE_INLINE | 0x0006,
|
||||
CMARK_NODE_EMPH = CMARK_NODE_TYPE_INLINE | 0x0007,
|
||||
CMARK_NODE_STRONG = CMARK_NODE_TYPE_INLINE | 0x0008,
|
||||
CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009,
|
||||
CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a,
|
||||
CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
|
||||
CMARK_NODE_ATTRIBUTE = CMARK_NODE_TYPE_INLINE | 0x000c,
|
||||
} cmark_node_type;
|
||||
|
||||
extern cmark_node_type CMARK_NODE_LAST_BLOCK;
|
||||
extern cmark_node_type CMARK_NODE_LAST_INLINE;
|
||||
|
||||
/* For backwards compatibility: */
|
||||
#define CMARK_NODE_HEADER CMARK_NODE_HEADING
|
||||
#define CMARK_NODE_HRULE CMARK_NODE_THEMATIC_BREAK
|
||||
#define CMARK_NODE_HTML CMARK_NODE_HTML_BLOCK
|
||||
#define CMARK_NODE_INLINE_HTML CMARK_NODE_HTML_INLINE
|
||||
|
||||
typedef enum {
|
||||
CMARK_NO_LIST,
|
||||
CMARK_BULLET_LIST,
|
||||
CMARK_ORDERED_LIST
|
||||
} cmark_list_type;
|
||||
|
||||
typedef enum {
|
||||
CMARK_NO_DELIM,
|
||||
CMARK_PERIOD_DELIM,
|
||||
CMARK_PAREN_DELIM
|
||||
} cmark_delim_type;
|
||||
|
||||
typedef struct cmark_node cmark_node;
|
||||
typedef struct cmark_parser cmark_parser;
|
||||
typedef struct cmark_iter cmark_iter;
|
||||
typedef struct cmark_syntax_extension cmark_syntax_extension;
|
||||
|
||||
/**
|
||||
* ## Custom memory allocator support
|
||||
*/
|
||||
|
||||
/** Defines the memory allocation functions to be used by CMark
|
||||
* when parsing and allocating a document tree
|
||||
*/
|
||||
typedef struct cmark_mem {
|
||||
void *(*calloc)(size_t, size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void (*free)(void *);
|
||||
} cmark_mem;
|
||||
|
||||
/** The default memory allocator; uses the system's calloc,
|
||||
* realloc and free.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_mem *cmark_get_default_mem_allocator(void);
|
||||
|
||||
/** An arena allocator; uses system calloc to allocate large
|
||||
* slabs of memory. Memory in these slabs is not reused at all.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_mem *cmark_get_arena_mem_allocator(void);
|
||||
|
||||
/** Resets the arena allocator, quickly returning all used memory
|
||||
* to the operating system.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_arena_reset(void);
|
||||
|
||||
/** Callback for freeing user data with a 'cmark_mem' context.
|
||||
*/
|
||||
typedef void (*cmark_free_func) (cmark_mem *mem, void *user_data);
|
||||
|
||||
|
||||
/*
|
||||
* ## Basic data structures
|
||||
*
|
||||
* To keep dependencies to the strict minimum, libcmark implements
|
||||
* its own versions of "classic" data structures.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ### Linked list
|
||||
*/
|
||||
|
||||
/** A generic singly linked list.
|
||||
*/
|
||||
typedef struct _cmark_llist
|
||||
{
|
||||
struct _cmark_llist *next;
|
||||
void *data;
|
||||
} cmark_llist;
|
||||
|
||||
/** Append an element to the linked list, return the possibly modified
|
||||
* head of the list.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_llist * cmark_llist_append (cmark_mem * mem,
|
||||
cmark_llist * head,
|
||||
void * data);
|
||||
|
||||
/** Free the list starting with 'head', calling 'free_func' with the
|
||||
* data pointer of each of its elements
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_llist_free_full (cmark_mem * mem,
|
||||
cmark_llist * head,
|
||||
cmark_free_func free_func);
|
||||
|
||||
/** Free the list starting with 'head'
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_llist_free (cmark_mem * mem,
|
||||
cmark_llist * head);
|
||||
|
||||
/**
|
||||
* ## Creating and Destroying Nodes
|
||||
*/
|
||||
|
||||
/** Creates a new node of type 'type'. Note that the node may have
|
||||
* other required properties, which it is the caller's responsibility
|
||||
* to assign.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_new(cmark_node_type type);
|
||||
|
||||
/** Same as `cmark_node_new`, but explicitly listing the memory
|
||||
* allocator used to allocate the node. Note: be sure to use the same
|
||||
* allocator for every node in a tree, or bad things can happen.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem(cmark_node_type type,
|
||||
cmark_mem *mem);
|
||||
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_ext(cmark_node_type type,
|
||||
cmark_syntax_extension *extension);
|
||||
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem_and_ext(cmark_node_type type,
|
||||
cmark_mem *mem,
|
||||
cmark_syntax_extension *extension);
|
||||
|
||||
/** Frees the memory allocated for a node and any children.
|
||||
*/
|
||||
CMARK_GFM_EXPORT void cmark_node_free(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Tree Traversal
|
||||
*/
|
||||
|
||||
/** Returns the next node in the sequence after 'node', or NULL if
|
||||
* there is none.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_next(cmark_node *node);
|
||||
|
||||
/** Returns the previous node in the sequence after 'node', or NULL if
|
||||
* there is none.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_previous(cmark_node *node);
|
||||
|
||||
/** Returns the parent of 'node', or NULL if there is none.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_parent(cmark_node *node);
|
||||
|
||||
/** Returns the first child of 'node', or NULL if 'node' has no children.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_first_child(cmark_node *node);
|
||||
|
||||
/** Returns the last child of 'node', or NULL if 'node' has no children.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_last_child(cmark_node *node);
|
||||
|
||||
/** Returns the N'th child of 'node', or NULL if 'node' does not have at least N children.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_nth_child(cmark_node *node, int n);
|
||||
|
||||
/** Returns the footnote reference of 'node', or NULL if 'node' doesn't have a
|
||||
* footnote reference.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node *cmark_node_parent_footnote_def(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Iterator
|
||||
*
|
||||
* An iterator will walk through a tree of nodes, starting from a root
|
||||
* node, returning one node at a time, together with information about
|
||||
* whether the node is being entered or exited. The iterator will
|
||||
* first descend to a child node, if there is one. When there is no
|
||||
* child, the iterator will go to the next sibling. When there is no
|
||||
* next sibling, the iterator will return to the parent (but with
|
||||
* a 'cmark_event_type' of `CMARK_EVENT_EXIT`). The iterator will
|
||||
* return `CMARK_EVENT_DONE` when it reaches the root node again.
|
||||
* One natural application is an HTML renderer, where an `ENTER` event
|
||||
* outputs an open tag and an `EXIT` event outputs a close tag.
|
||||
* An iterator might also be used to transform an AST in some systematic
|
||||
* way, for example, turning all level-3 headings into regular paragraphs.
|
||||
*
|
||||
* void
|
||||
* usage_example(cmark_node *root) {
|
||||
* cmark_event_type ev_type;
|
||||
* cmark_iter *iter = cmark_iter_new(root);
|
||||
*
|
||||
* while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
* cmark_node *cur = cmark_iter_get_node(iter);
|
||||
* // Do something with `cur` and `ev_type`
|
||||
* }
|
||||
*
|
||||
* cmark_iter_free(iter);
|
||||
* }
|
||||
*
|
||||
* Iterators will never return `EXIT` events for leaf nodes, which are nodes
|
||||
* of type:
|
||||
*
|
||||
* * CMARK_NODE_HTML_BLOCK
|
||||
* * CMARK_NODE_THEMATIC_BREAK
|
||||
* * CMARK_NODE_CODE_BLOCK
|
||||
* * CMARK_NODE_TEXT
|
||||
* * CMARK_NODE_SOFTBREAK
|
||||
* * CMARK_NODE_LINEBREAK
|
||||
* * CMARK_NODE_CODE
|
||||
* * CMARK_NODE_HTML_INLINE
|
||||
*
|
||||
* Nodes must only be modified after an `EXIT` event, or an `ENTER` event for
|
||||
* leaf nodes.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
CMARK_EVENT_NONE,
|
||||
CMARK_EVENT_DONE,
|
||||
CMARK_EVENT_ENTER,
|
||||
CMARK_EVENT_EXIT
|
||||
} cmark_event_type;
|
||||
|
||||
/** Creates a new iterator starting at 'root'. The current node and event
|
||||
* type are undefined until 'cmark_iter_next' is called for the first time.
|
||||
* The memory allocated for the iterator should be released using
|
||||
* 'cmark_iter_free' when it is no longer needed.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_iter *cmark_iter_new(cmark_node *root);
|
||||
|
||||
/** Frees the memory allocated for an iterator.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_iter_free(cmark_iter *iter);
|
||||
|
||||
/** Advances to the next node and returns the event type (`CMARK_EVENT_ENTER`,
|
||||
* `CMARK_EVENT_EXIT` or `CMARK_EVENT_DONE`).
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_event_type cmark_iter_next(cmark_iter *iter);
|
||||
|
||||
/** Returns the current node.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node *cmark_iter_get_node(cmark_iter *iter);
|
||||
|
||||
/** Returns the current event type.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_event_type cmark_iter_get_event_type(cmark_iter *iter);
|
||||
|
||||
/** Returns the root node.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node *cmark_iter_get_root(cmark_iter *iter);
|
||||
|
||||
/** Resets the iterator so that the current node is 'current' and
|
||||
* the event type is 'event_type'. The new current node must be a
|
||||
* descendant of the root node or the root node itself.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
|
||||
cmark_event_type event_type);
|
||||
|
||||
/**
|
||||
* ## Accessors
|
||||
*/
|
||||
|
||||
/** Returns the user data of 'node'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT void *cmark_node_get_user_data(cmark_node *node);
|
||||
|
||||
/** Sets arbitrary user data for 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_user_data(cmark_node *node, void *user_data);
|
||||
|
||||
/** Set free function for user data */
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_node_set_user_data_free_func(cmark_node *node,
|
||||
cmark_free_func free_func);
|
||||
|
||||
/** Returns the type of 'node', or `CMARK_NODE_NONE` on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_node_type cmark_node_get_type(cmark_node *node);
|
||||
|
||||
/** Like 'cmark_node_get_type', but returns a string representation
|
||||
of the type, or `"<unknown>"`.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
const char *cmark_node_get_type_string(cmark_node *node);
|
||||
|
||||
/** Returns the string contents of 'node', or an empty
|
||||
string if none is set. Returns NULL if called on a
|
||||
node that does not have string content.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_literal(cmark_node *node);
|
||||
|
||||
/** Returns the number of backtick characters used to open the
|
||||
node if it is an inline code span, otherwise returns 0.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_backtick_count(cmark_node *node);
|
||||
|
||||
/** Sets the string contents of 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_literal(cmark_node *node, const char *content);
|
||||
|
||||
/** Returns the heading level of 'node', or 0 if 'node' is not a heading.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_heading_level(cmark_node *node);
|
||||
|
||||
/* For backwards compatibility */
|
||||
#define cmark_node_get_header_level cmark_node_get_heading_level
|
||||
#define cmark_node_set_header_level cmark_node_set_heading_level
|
||||
|
||||
/** Sets the heading level of 'node', returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_heading_level(cmark_node *node, int level);
|
||||
|
||||
/** Returns the list type of 'node', or `CMARK_NO_LIST` if 'node'
|
||||
* is not a list.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_list_type cmark_node_get_list_type(cmark_node *node);
|
||||
|
||||
/** Sets the list type of 'node', returning 1 on success and 0 on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_list_type(cmark_node *node,
|
||||
cmark_list_type type);
|
||||
|
||||
/** Returns the list delimiter type of 'node', or `CMARK_NO_DELIM` if 'node'
|
||||
* is not a list.
|
||||
*/
|
||||
CMARK_GFM_EXPORT cmark_delim_type cmark_node_get_list_delim(cmark_node *node);
|
||||
|
||||
/** Sets the list delimiter type of 'node', returning 1 on success and 0
|
||||
* on error.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_list_delim(cmark_node *node,
|
||||
cmark_delim_type delim);
|
||||
|
||||
/** Returns starting number of 'node', if it is an ordered list, otherwise 0.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_list_start(cmark_node *node);
|
||||
|
||||
/** Sets starting number of 'node', if it is an ordered list. Returns 1
|
||||
* on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_list_start(cmark_node *node, int start);
|
||||
|
||||
/** Returns 1 if 'node' is a tight list, 0 otherwise.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_list_tight(cmark_node *node);
|
||||
|
||||
/** Sets the "tightness" of a list. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight);
|
||||
|
||||
/**
|
||||
* Returns item index of 'node'. This is only used when rendering output
|
||||
* formats such as commonmark, which need to output the index. It is not
|
||||
* required for formats such as html or latex.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node);
|
||||
|
||||
/** Sets item index of 'node'. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx);
|
||||
|
||||
/** Returns the info string from a fenced code block.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);
|
||||
|
||||
/** Sets the info string in a fenced code block, returning 1 on
|
||||
* success and 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_fence_info(cmark_node *node, const char *info);
|
||||
|
||||
/** Sets code blocks fencing details
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_fenced(cmark_node * node, int fenced,
|
||||
int length, int offset, char character);
|
||||
|
||||
/** Returns code blocks fencing details
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_fenced(cmark_node *node, int *length, int *offset, char *character);
|
||||
|
||||
/** Returns the URL of a link or image 'node', or an empty string
|
||||
if no URL is set. Returns NULL if called on a node that is
|
||||
not a link or image.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_url(cmark_node *node);
|
||||
|
||||
/** Sets the URL of a link or image 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_url(cmark_node *node, const char *url);
|
||||
|
||||
/** Returns the title of a link or image 'node', or an empty
|
||||
string if no title is set. Returns NULL if called on a node
|
||||
that is not a link or image.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_title(cmark_node *node);
|
||||
|
||||
/** Sets the title of a link or image 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_title(cmark_node *node, const char *title);
|
||||
|
||||
/** Returns the attributes of an attribute 'node', or an empty string
|
||||
if no attributes are set. Returns NULL if called on a node that is
|
||||
not an attribute.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_attributes(cmark_node *node);
|
||||
|
||||
/** Sets the attributes of an attribute 'node'. Returns 1 on success,
|
||||
* 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_attributes(cmark_node *node, const char *attributes);
|
||||
|
||||
/** Returns the literal "on enter" text for a custom 'node', or
|
||||
an empty string if no on_enter is set. Returns NULL if called
|
||||
on a non-custom node.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_on_enter(cmark_node *node);
|
||||
|
||||
/** Sets the literal text to render "on enter" for a custom 'node'.
|
||||
Any children of the node will be rendered after this text.
|
||||
Returns 1 on success 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_on_enter(cmark_node *node,
|
||||
const char *on_enter);
|
||||
|
||||
/** Returns the literal "on exit" text for a custom 'node', or
|
||||
an empty string if no on_exit is set. Returns NULL if
|
||||
called on a non-custom node.
|
||||
*/
|
||||
CMARK_GFM_EXPORT const char *cmark_node_get_on_exit(cmark_node *node);
|
||||
|
||||
/** Sets the literal text to render "on exit" for a custom 'node'.
|
||||
Any children of the node will be rendered before this text.
|
||||
Returns 1 on success 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_set_on_exit(cmark_node *node, const char *on_exit);
|
||||
|
||||
/** Returns the line on which 'node' begins.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_start_line(cmark_node *node);
|
||||
|
||||
/** Returns the column at which 'node' begins.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_start_column(cmark_node *node);
|
||||
|
||||
/** Returns the line on which 'node' ends.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_end_line(cmark_node *node);
|
||||
|
||||
/** Returns the column at which 'node' ends.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_get_end_column(cmark_node *node);
|
||||
|
||||
/**
|
||||
* ## Tree Manipulation
|
||||
*/
|
||||
|
||||
/** Unlinks a 'node', removing it from the tree, but not freeing its
|
||||
* memory. (Use 'cmark_node_free' for that.)
|
||||
*/
|
||||
CMARK_GFM_EXPORT void cmark_node_unlink(cmark_node *node);
|
||||
|
||||
/** Inserts 'sibling' before 'node'. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_insert_before(cmark_node *node,
|
||||
cmark_node *sibling);
|
||||
|
||||
/** Inserts 'sibling' after 'node'. Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_insert_after(cmark_node *node, cmark_node *sibling);
|
||||
|
||||
/** Replaces 'oldnode' with 'newnode' and unlinks 'oldnode' (but does
|
||||
* not free its memory).
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_replace(cmark_node *oldnode, cmark_node *newnode);
|
||||
|
||||
/** Adds 'child' to the beginning of the children of 'node'.
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_prepend_child(cmark_node *node, cmark_node *child);
|
||||
|
||||
/** Adds 'child' to the end of the children of 'node'.
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
CMARK_GFM_EXPORT int cmark_node_append_child(cmark_node *node, cmark_node *child);
|
||||
|
||||
/** Consolidates adjacent text nodes.
|
||||
*/
|
||||
CMARK_GFM_EXPORT void cmark_consolidate_text_nodes(cmark_node *root);
|
||||
|
||||
/** Ensures a node and all its children own their own chunk memory.
|
||||
*/
|
||||
CMARK_GFM_EXPORT void cmark_node_own(cmark_node *root);
|
||||
|
||||
/**
|
||||
* ## Parsing
|
||||
*
|
||||
* Simple interface:
|
||||
*
|
||||
* cmark_node *document = cmark_parse_document("Hello *world*", 13,
|
||||
* CMARK_OPT_DEFAULT);
|
||||
*
|
||||
* Streaming interface:
|
||||
*
|
||||
* cmark_parser *parser = cmark_parser_new(CMARK_OPT_DEFAULT);
|
||||
* FILE *fp = fopen("myfile.md", "rb");
|
||||
* while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
|
||||
* cmark_parser_feed(parser, buffer, bytes);
|
||||
* if (bytes < sizeof(buffer)) {
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* document = cmark_parser_finish(parser);
|
||||
* cmark_parser_free(parser);
|
||||
*/
|
||||
|
||||
/** Creates a new parser object.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_parser *cmark_parser_new(int options);
|
||||
|
||||
/** Creates a new parser object with the given memory allocator
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem);
|
||||
|
||||
/** Frees memory allocated for a parser object.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parser_free(cmark_parser *parser);
|
||||
|
||||
/** Feeds a string of length 'len' to 'parser'.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len);
|
||||
|
||||
/** Finish parsing and return a pointer to a tree of nodes.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node *cmark_parser_finish(cmark_parser *parser);
|
||||
|
||||
/** Parse a CommonMark document in 'buffer' of length 'len'.
|
||||
* Returns a pointer to a tree of nodes. The memory allocated for
|
||||
* the node tree should be released using 'cmark_node_free'
|
||||
* when it is no longer needed.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node *cmark_parse_document(const char *buffer, size_t len, int options);
|
||||
|
||||
/** Parse a CommonMark document in file 'f', returning a pointer to
|
||||
* a tree of nodes. The memory allocated for the node tree should be
|
||||
* released using 'cmark_node_free' when it is no longer needed.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_node *cmark_parse_file(FILE *f, int options);
|
||||
|
||||
/**
|
||||
* ## Rendering
|
||||
*/
|
||||
|
||||
/** Render a 'node' tree as XML. It is the caller's responsibility
|
||||
* to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_xml(cmark_node *root, int options);
|
||||
|
||||
/** As for 'cmark_render_xml', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem);
|
||||
|
||||
/** Render a 'node' tree as an HTML fragment. It is up to the user
|
||||
* to add an appropriate header and footer. It is the caller's
|
||||
* responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_html(cmark_node *root, int options, cmark_llist *extensions);
|
||||
|
||||
/** As for 'cmark_render_html', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_html_with_mem(cmark_node *root, int options, cmark_llist *extensions, cmark_mem *mem);
|
||||
|
||||
/** Render a 'node' tree as a groff man page, without the header.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_man(cmark_node *root, int options, int width);
|
||||
|
||||
/** As for 'cmark_render_man', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_man_with_mem(cmark_node *root, int options, int width, cmark_mem *mem);
|
||||
|
||||
/** Render a 'node' tree as a commonmark document.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_commonmark(cmark_node *root, int options, int width);
|
||||
|
||||
/** As for 'cmark_render_commonmark', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_commonmark_with_mem(cmark_node *root, int options, int width, cmark_mem *mem);
|
||||
|
||||
/** Render a 'node' tree as a plain text document.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_plaintext(cmark_node *root, int options, int width);
|
||||
|
||||
/** As for 'cmark_render_plaintext', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_plaintext_with_mem(cmark_node *root, int options, int width, cmark_mem *mem);
|
||||
|
||||
/** Render a 'node' tree as a LaTeX document.
|
||||
* It is the caller's responsibility to free the returned buffer.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_latex(cmark_node *root, int options, int width);
|
||||
|
||||
/** As for 'cmark_render_latex', but specifying the allocator to use for
|
||||
* the resulting string.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmark_mem *mem);
|
||||
|
||||
/**
|
||||
* ## Options
|
||||
*/
|
||||
|
||||
/** Default options.
|
||||
*/
|
||||
#define CMARK_OPT_DEFAULT 0
|
||||
|
||||
/**
|
||||
* ### Options affecting rendering
|
||||
*/
|
||||
|
||||
/** Include a `data-sourcepos` attribute on all block elements.
|
||||
*/
|
||||
#define CMARK_OPT_SOURCEPOS (1 << 1)
|
||||
|
||||
/** Render `softbreak` elements as hard line breaks.
|
||||
*/
|
||||
#define CMARK_OPT_HARDBREAKS (1 << 2)
|
||||
|
||||
/** `CMARK_OPT_SAFE` is defined here for API compatibility,
|
||||
but it no longer has any effect. "Safe" mode is now the default:
|
||||
set `CMARK_OPT_UNSAFE` to disable it.
|
||||
*/
|
||||
#define CMARK_OPT_SAFE (1 << 3)
|
||||
|
||||
/** Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
|
||||
* `file:`, and `data:`, except for `image/png`, `image/gif`,
|
||||
* `image/jpeg`, or `image/webp` mime types). By default,
|
||||
* raw HTML is replaced by a placeholder HTML comment. Unsafe
|
||||
* links are replaced by empty strings.
|
||||
*/
|
||||
#define CMARK_OPT_UNSAFE (1 << 17)
|
||||
|
||||
/** Render `softbreak` elements as spaces.
|
||||
*/
|
||||
#define CMARK_OPT_NOBREAKS (1 << 4)
|
||||
|
||||
/**
|
||||
* ### Options affecting parsing
|
||||
*/
|
||||
|
||||
/** Legacy option (no effect).
|
||||
*/
|
||||
#define CMARK_OPT_NORMALIZE (1 << 8)
|
||||
|
||||
/** Validate UTF-8 in the input before parsing, replacing illegal
|
||||
* sequences with the replacement character U+FFFD.
|
||||
*/
|
||||
#define CMARK_OPT_VALIDATE_UTF8 (1 << 9)
|
||||
|
||||
/** Convert straight quotes to curly, --- to em dashes, -- to en dashes.
|
||||
*/
|
||||
#define CMARK_OPT_SMART (1 << 10)
|
||||
|
||||
/** Use GitHub-style <pre lang="x"> tags for code blocks instead of <pre><code
|
||||
* class="language-x">.
|
||||
*/
|
||||
#define CMARK_OPT_GITHUB_PRE_LANG (1 << 11)
|
||||
|
||||
/** Be liberal in interpreting inline HTML tags.
|
||||
*/
|
||||
#define CMARK_OPT_LIBERAL_HTML_TAG (1 << 12)
|
||||
|
||||
/** Parse footnotes.
|
||||
*/
|
||||
#define CMARK_OPT_FOOTNOTES (1 << 13)
|
||||
|
||||
/** Only parse strikethroughs if surrounded by exactly 2 tildes.
|
||||
* Gives some compatibility with redcarpet.
|
||||
*/
|
||||
#define CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE (1 << 14)
|
||||
|
||||
/** Use style attributes to align table cells instead of align attributes.
|
||||
*/
|
||||
#define CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES (1 << 15)
|
||||
|
||||
/** Include the remainder of the info string in code blocks in
|
||||
* a separate attribute.
|
||||
*/
|
||||
#define CMARK_OPT_FULL_INFO_STRING (1 << 16)
|
||||
|
||||
/** Parse only inline markdown directives. Block directives will not be
|
||||
* parsed (their literal representations will remain in the output).
|
||||
*/
|
||||
#define CMARK_OPT_INLINE_ONLY (1 << 18)
|
||||
|
||||
/** Parse the markdown input without removing preceding/trailing whitespace and
|
||||
* without converting newline characters to breaks. Using this option also
|
||||
* enables the CMARK_OPT_INLINE_ONLY option.
|
||||
*/
|
||||
#define CMARK_OPT_PRESERVE_WHITESPACE ((1 << 19) | CMARK_OPT_INLINE_ONLY)
|
||||
|
||||
/** Parse row- and column-span in tables.
|
||||
*/
|
||||
#define CMARK_OPT_TABLE_SPANS (1 << 20)
|
||||
|
||||
/** Parse table cells defining row span using a double-quote symbol (`"`, or "ditto mark")
|
||||
* instead of the default caret symbol (`^`).
|
||||
*
|
||||
* Does nothing unless \c CMARK_OPT_TABLE_SPANS is also set.
|
||||
*/
|
||||
#define CMARK_OPT_TABLE_ROWSPAN_DITTO (1 << 21)
|
||||
|
||||
/**
|
||||
* ## Version information
|
||||
*/
|
||||
|
||||
/** The library version as integer for runtime checks. Also available as
|
||||
* macro CMARK_VERSION for compile time checks.
|
||||
*
|
||||
* * Bits 16-23 contain the major version.
|
||||
* * Bits 8-15 contain the minor version.
|
||||
* * Bits 0-7 contain the patchlevel.
|
||||
*
|
||||
* In hexadecimal format, the number 0x010203 represents version 1.2.3.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_version(void);
|
||||
|
||||
/** The library version string for runtime checks. Also available as
|
||||
* macro CMARK_VERSION_STRING for compile time checks.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
const char *cmark_version_string(void);
|
||||
|
||||
/** # AUTHORS
|
||||
*
|
||||
* John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer.
|
||||
*/
|
||||
|
||||
#if !defined(CMARK_NO_SHORT_NAMES)
|
||||
#define NODE_DOCUMENT CMARK_NODE_DOCUMENT
|
||||
#define NODE_BLOCK_QUOTE CMARK_NODE_BLOCK_QUOTE
|
||||
#define NODE_LIST CMARK_NODE_LIST
|
||||
#define NODE_ITEM CMARK_NODE_ITEM
|
||||
#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
|
||||
#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
|
||||
#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
|
||||
#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH
|
||||
#define NODE_HEADING CMARK_NODE_HEADING
|
||||
#define NODE_HEADER CMARK_NODE_HEADER
|
||||
#define NODE_THEMATIC_BREAK CMARK_NODE_THEMATIC_BREAK
|
||||
#define NODE_HRULE CMARK_NODE_HRULE
|
||||
#define NODE_TEXT CMARK_NODE_TEXT
|
||||
#define NODE_SOFTBREAK CMARK_NODE_SOFTBREAK
|
||||
#define NODE_LINEBREAK CMARK_NODE_LINEBREAK
|
||||
#define NODE_CODE CMARK_NODE_CODE
|
||||
#define NODE_HTML_INLINE CMARK_NODE_HTML_INLINE
|
||||
#define NODE_CUSTOM_INLINE CMARK_NODE_CUSTOM_INLINE
|
||||
#define NODE_EMPH CMARK_NODE_EMPH
|
||||
#define NODE_STRONG CMARK_NODE_STRONG
|
||||
#define NODE_LINK CMARK_NODE_LINK
|
||||
#define NODE_IMAGE CMARK_NODE_IMAGE
|
||||
#define BULLET_LIST CMARK_BULLET_LIST
|
||||
#define ORDERED_LIST CMARK_ORDERED_LIST
|
||||
#define PERIOD_DELIM CMARK_PERIOD_DELIM
|
||||
#define PAREN_DELIM CMARK_PAREN_DELIM
|
||||
#endif
|
||||
|
||||
typedef int32_t bufsize_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,7 +0,0 @@
|
||||
#ifndef CMARK_GFM_VERSION_H
|
||||
#define CMARK_GFM_VERSION_H
|
||||
|
||||
#define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 13)
|
||||
#define CMARK_GFM_VERSION_STRING "0.29.0.gfm.13"
|
||||
|
||||
#endif
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef CMARK_CMARK_CTYPE_H
|
||||
#define CMARK_CMARK_CTYPE_H
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Locale-independent versions of functions from ctype.h.
|
||||
* We want cmark to behave the same no matter what the system locale.
|
||||
*/
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_isspace(char c);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_ispunct(char c);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_isalnum(char c);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_isdigit(char c);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_isalpha(char c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef CMARK_GFM_EXPORT_H
|
||||
#define CMARK_GFM_EXPORT_H
|
||||
|
||||
#ifdef CMARK_GFM_STATIC_DEFINE
|
||||
# define CMARK_GFM_EXPORT
|
||||
# define CMARK_GFM_NO_EXPORT
|
||||
#else
|
||||
# if defined(_WIN32)
|
||||
# if defined(libcmark_gfm_EXPORTS)
|
||||
# define CMARK_GFM_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define CMARK_GFM_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# define CMARK_GFM_NO_EXPORT
|
||||
# else
|
||||
# if defined(libcmark_gfm_EXPORTS)
|
||||
# define CMARK_GFM_EXPORT __attribute__((__visibility__("default")))
|
||||
# else
|
||||
# define CMARK_GFM_EXPORT __attribute__((__visibility__("default")))
|
||||
# endif
|
||||
# define CMARK_GFM_NO_EXPORT __attribute__((__visibility__("hidden")))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CMARK_GFM_DEPRECATED
|
||||
# if defined(_WIN32)
|
||||
# define CMARK_GFM_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define CMARK_GFM_DEPRECATED __attribute__ ((__deprecated__))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef CMARK_GFM_DEPRECATED_EXPORT
|
||||
# define CMARK_GFM_DEPRECATED_EXPORT CMARK_GFM_EXPORT CMARK_GFM_DEPRECATED
|
||||
#endif
|
||||
|
||||
#ifndef CMARK_GFM_DEPRECATED_NO_EXPORT
|
||||
# define CMARK_GFM_DEPRECATED_NO_EXPORT CMARK_GFM_NO_EXPORT CMARK_GFM_DEPRECATED
|
||||
#endif
|
||||
|
||||
#endif /* not CMARK_GFM_EXPORT_H */
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef CMARK_FOOTNOTES_H
|
||||
#define CMARK_FOOTNOTES_H
|
||||
|
||||
#include "map.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct cmark_footnote {
|
||||
cmark_map_entry entry;
|
||||
cmark_node *node;
|
||||
unsigned int ix;
|
||||
};
|
||||
|
||||
typedef struct cmark_footnote cmark_footnote;
|
||||
|
||||
void cmark_footnote_create(cmark_map *map, cmark_node *node);
|
||||
cmark_map *cmark_footnote_map_new(cmark_mem *mem);
|
||||
|
||||
void cmark_unlink_footnotes_map(cmark_map *map);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef CMARK_HOUDINI_H
|
||||
#define CMARK_HOUDINI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef HOUDINI_USE_LOCALE
|
||||
#define _isxdigit(c) isxdigit(c)
|
||||
#define _isdigit(c) isdigit(c)
|
||||
#else
|
||||
/*
|
||||
* Helper _isdigit methods -- do not trust the current locale
|
||||
* */
|
||||
#define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)
|
||||
#define _isdigit(c) ((c) >= '0' && (c) <= '9')
|
||||
#endif
|
||||
|
||||
#define HOUDINI_ESCAPED_SIZE(x) (((x)*12) / 10)
|
||||
#define HOUDINI_UNESCAPED_SIZE(x) (x)
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
bufsize_t houdini_unescape_ent(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
CMARK_GFM_EXPORT
|
||||
int houdini_escape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
CMARK_GFM_EXPORT
|
||||
int houdini_escape_html0(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size, int secure);
|
||||
CMARK_GFM_EXPORT
|
||||
int houdini_unescape_html(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
CMARK_GFM_EXPORT
|
||||
void houdini_unescape_html_f(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
CMARK_GFM_EXPORT
|
||||
int houdini_escape_href(cmark_strbuf *ob, const uint8_t *src,
|
||||
bufsize_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef CMARK_HTML_H
|
||||
#define CMARK_HTML_H
|
||||
|
||||
#include "buffer.h"
|
||||
#include "node.h"
|
||||
|
||||
inline
|
||||
static void cmark_html_render_cr(cmark_strbuf *html) {
|
||||
if (html->size && html->ptr[html->size - 1] != '\n')
|
||||
cmark_strbuf_putc(html, '\n');
|
||||
}
|
||||
|
||||
#define BUFFER_SIZE 100
|
||||
|
||||
inline
|
||||
static void cmark_html_render_sourcepos(cmark_node *node, cmark_strbuf *html, int options) {
|
||||
char buffer[BUFFER_SIZE];
|
||||
if (CMARK_OPT_SOURCEPOS & options) {
|
||||
snprintf(buffer, BUFFER_SIZE, " data-sourcepos=\"%d:%d-%d:%d\"",
|
||||
cmark_node_get_start_line(node), cmark_node_get_start_column(node),
|
||||
cmark_node_get_end_line(node), cmark_node_get_end_column(node));
|
||||
cmark_strbuf_puts(html, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifndef CMARK_INLINES_H
|
||||
#define CMARK_INLINES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "references.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url);
|
||||
cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title);
|
||||
cmark_chunk cmark_clean_attributes(cmark_mem *mem, cmark_chunk *attributes);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_parse_inlines(cmark_parser *parser,
|
||||
cmark_node *parent,
|
||||
cmark_map *refmap,
|
||||
int options);
|
||||
|
||||
bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_chunk *input,
|
||||
cmark_map *refmap);
|
||||
|
||||
bufsize_t cmark_parse_reference_attributes_inline(cmark_mem *mem, cmark_chunk *input,
|
||||
cmark_map *refmap);
|
||||
|
||||
void cmark_inlines_add_special_character(cmark_parser *parser, unsigned char c, bool emphasis);
|
||||
void cmark_inlines_remove_special_character(cmark_parser *parser, unsigned char c, bool emphasis);
|
||||
|
||||
void cmark_set_default_skip_chars(int8_t **skip_chars, bool use_memcpy);
|
||||
void cmark_set_default_special_chars(int8_t **special_chars, bool use_memcpy);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef CMARK_ITERATOR_H
|
||||
#define CMARK_ITERATOR_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *node;
|
||||
} cmark_iter_state;
|
||||
|
||||
struct cmark_iter {
|
||||
cmark_mem *mem;
|
||||
cmark_node *root;
|
||||
cmark_iter_state cur;
|
||||
cmark_iter_state next;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef CMARK_MAP_H
|
||||
#define CMARK_MAP_H
|
||||
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct cmark_map_entry {
|
||||
struct cmark_map_entry *next;
|
||||
unsigned char *label;
|
||||
size_t age;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef struct cmark_map_entry cmark_map_entry;
|
||||
|
||||
struct cmark_map;
|
||||
|
||||
typedef void (*cmark_map_free_f)(struct cmark_map *, cmark_map_entry *);
|
||||
|
||||
struct cmark_map {
|
||||
cmark_mem *mem;
|
||||
cmark_map_entry *refs;
|
||||
cmark_map_entry **sorted;
|
||||
size_t size;
|
||||
size_t ref_size;
|
||||
size_t max_ref_size;
|
||||
cmark_map_free_f free;
|
||||
};
|
||||
|
||||
typedef struct cmark_map cmark_map;
|
||||
|
||||
unsigned char *normalize_map_label(cmark_mem *mem, cmark_chunk *ref);
|
||||
cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free);
|
||||
void cmark_map_free(cmark_map *map);
|
||||
cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,23 +0,0 @@
|
||||
module cmark_gfm {
|
||||
header "cmark-gfm.h"
|
||||
header "cmark-gfm-extension_api.h"
|
||||
header "buffer.h"
|
||||
header "chunk.h"
|
||||
header "cmark_ctype.h"
|
||||
header "footnotes.h"
|
||||
header "houdini.h"
|
||||
header "html.h"
|
||||
header "inlines.h"
|
||||
header "iterator.h"
|
||||
header "map.h"
|
||||
header "node.h"
|
||||
header "parser.h"
|
||||
header "plugin.h"
|
||||
header "references.h"
|
||||
header "registry.h"
|
||||
header "render.h"
|
||||
header "scanners.h"
|
||||
header "syntax_extension.h"
|
||||
header "utf8.h"
|
||||
export *
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#ifndef CMARK_MUTEX_H
|
||||
#define CMARK_MUTEX_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef CMARK_THREADING
|
||||
|
||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined (_POSIX_THREADS)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define CMARK_DEFINE_ONCE(NAME) static pthread_once_t NAME##_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
#define CMARK_RUN_ONCE(NAME, FUNC) pthread_once(&NAME##_once, FUNC)
|
||||
|
||||
#define CMARK_DEFINE_LOCK(NAME) \
|
||||
static pthread_mutex_t NAME##_lock; \
|
||||
CMARK_DEFINE_ONCE(NAME); \
|
||||
static void initialize_##NAME() { pthread_mutex_init(&NAME##_lock, NULL); }
|
||||
|
||||
#define CMARK_INITIALIZE_AND_LOCK(NAME) \
|
||||
CMARK_RUN_ONCE(NAME, initialize_##NAME); \
|
||||
pthread_mutex_lock(&NAME##_lock);
|
||||
|
||||
#define CMARK_UNLOCK(NAME) pthread_mutex_unlock(&NAME##_lock);
|
||||
|
||||
#elif defined(_WIN32) // building for windows
|
||||
|
||||
#define _WIN32_WINNT 0x0600 // minimum target of Windows Vista
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define CMARK_DEFINE_ONCE(NAME) static INIT_ONCE NAME##_once = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
#define CMARK_RUN_ONCE(NAME, FUNC) do { \
|
||||
BOOL fStatus; BOOL fPending; \
|
||||
fStatus = InitOnceBeginInitialize(&NAME##_once, 0, &fPending, NULL); \
|
||||
if (!fStatus || !fPending) break; \
|
||||
FUNC(); \
|
||||
InitOnceComplete(&NAME##_once, 0, NULL); \
|
||||
} while (0);
|
||||
|
||||
#define CMARK_DEFINE_LOCK(NAME) static SRWLOCK NAME##_lock = SRWLOCK_INIT;
|
||||
|
||||
#define CMARK_INITIALIZE_AND_LOCK(NAME) AcquireSRWLockExclusive(&NAME##_lock);
|
||||
|
||||
#define CMARK_UNLOCK(NAME) ReleaseSRWLockExclusive(&NAME##_lock);
|
||||
|
||||
#endif
|
||||
|
||||
#else // no threading support
|
||||
|
||||
static inline bool check_latch(int *latch) {
|
||||
if (!*latch) {
|
||||
*latch = 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define CMARK_DEFINE_LOCK(NAME)
|
||||
#define CMARK_INITIALIZE_AND_LOCK(NAME)
|
||||
#define CMARK_UNLOCK(NAME)
|
||||
|
||||
#define CMARK_DEFINE_ONCE(NAME) static int NAME = 0;
|
||||
|
||||
#define CMARK_RUN_ONCE(NAME, FUNC) if (check_latch(&NAME)) FUNC();
|
||||
|
||||
#endif // CMARK_THREADING
|
||||
|
||||
#endif // CMARK_MUTEX_H
|
||||
@@ -1,173 +0,0 @@
|
||||
#ifndef CMARK_NODE_H
|
||||
#define CMARK_NODE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
#include "buffer.h"
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
cmark_list_type list_type;
|
||||
int marker_offset;
|
||||
int padding;
|
||||
int start;
|
||||
cmark_delim_type delimiter;
|
||||
unsigned char bullet_char;
|
||||
bool tight;
|
||||
bool checked; // For task list extension
|
||||
} cmark_list;
|
||||
|
||||
typedef struct {
|
||||
cmark_chunk info;
|
||||
cmark_chunk literal;
|
||||
uint8_t fence_length;
|
||||
uint8_t fence_offset;
|
||||
unsigned char fence_char;
|
||||
int8_t fenced;
|
||||
} cmark_code;
|
||||
|
||||
typedef struct {
|
||||
int level;
|
||||
bool setext;
|
||||
} cmark_heading;
|
||||
|
||||
typedef struct {
|
||||
cmark_chunk url;
|
||||
cmark_chunk title;
|
||||
} cmark_link;
|
||||
|
||||
typedef struct {
|
||||
cmark_chunk attributes;
|
||||
} cmark_attribute;
|
||||
|
||||
typedef struct {
|
||||
cmark_chunk on_enter;
|
||||
cmark_chunk on_exit;
|
||||
} cmark_custom;
|
||||
|
||||
enum cmark_node__internal_flags {
|
||||
CMARK_NODE__OPEN = (1 << 0),
|
||||
CMARK_NODE__LAST_LINE_BLANK = (1 << 1),
|
||||
CMARK_NODE__LAST_LINE_CHECKED = (1 << 2),
|
||||
|
||||
// Extensions can register custom flags by calling `cmark_register_node_flag`.
|
||||
// This is the starting value for the custom flags.
|
||||
CMARK_NODE__REGISTER_FIRST = (1 << 3),
|
||||
};
|
||||
|
||||
typedef uint16_t cmark_node_internal_flags;
|
||||
|
||||
struct cmark_node {
|
||||
cmark_strbuf content;
|
||||
|
||||
struct cmark_node *next;
|
||||
struct cmark_node *prev;
|
||||
struct cmark_node *parent;
|
||||
struct cmark_node *first_child;
|
||||
struct cmark_node *last_child;
|
||||
|
||||
void *user_data;
|
||||
cmark_free_func user_data_free_func;
|
||||
|
||||
int start_line;
|
||||
int start_column;
|
||||
int end_line;
|
||||
int end_column;
|
||||
int internal_offset;
|
||||
uint16_t type;
|
||||
cmark_node_internal_flags flags;
|
||||
int backtick_count;
|
||||
|
||||
cmark_syntax_extension *extension;
|
||||
|
||||
/**
|
||||
* Used during cmark_render() to cache the most recent non-NULL
|
||||
* extension, if you go up the parent chain like this:
|
||||
*
|
||||
* node->parent->...parent->extension
|
||||
*/
|
||||
cmark_syntax_extension *ancestor_extension;
|
||||
|
||||
union {
|
||||
int ref_ix;
|
||||
int def_count;
|
||||
} footnote;
|
||||
|
||||
cmark_node *parent_footnote_def;
|
||||
|
||||
union {
|
||||
cmark_chunk literal;
|
||||
cmark_list list;
|
||||
cmark_code code;
|
||||
cmark_heading heading;
|
||||
cmark_link link;
|
||||
cmark_attribute attribute;
|
||||
cmark_custom custom;
|
||||
int html_block_type;
|
||||
void *opaque;
|
||||
} as;
|
||||
};
|
||||
|
||||
/**
|
||||
* Syntax extensions can use this function to register a custom node
|
||||
* flag. The flags are stored in the `flags` field of the `cmark_node`
|
||||
* struct. The `flags` parameter should be the address of a global variable
|
||||
* which will store the flag value.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_register_node_flag(cmark_node_internal_flags *flags);
|
||||
|
||||
/**
|
||||
* DEPRECATED.
|
||||
*
|
||||
* This function was added in cmark-gfm version 0.29.0.gfm.7, and was
|
||||
* required to be called at program start time, which caused
|
||||
* backwards-compatibility issues in applications that use cmark-gfm as a
|
||||
* library. It is now a no-op.
|
||||
*/
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_init_standard_node_flags(void);
|
||||
|
||||
static inline cmark_mem *cmark_node_mem(cmark_node *node) {
|
||||
return node->content.mem;
|
||||
}
|
||||
CMARK_GFM_EXPORT int cmark_node_check(cmark_node *node, FILE *out);
|
||||
|
||||
static inline bool CMARK_NODE_TYPE_BLOCK_P(cmark_node_type node_type) {
|
||||
return (node_type & CMARK_NODE_TYPE_MASK) == CMARK_NODE_TYPE_BLOCK;
|
||||
}
|
||||
|
||||
static inline bool CMARK_NODE_BLOCK_P(cmark_node *node) {
|
||||
return node != NULL && CMARK_NODE_TYPE_BLOCK_P((cmark_node_type) node->type);
|
||||
}
|
||||
|
||||
static inline bool CMARK_NODE_TYPE_INLINE_P(cmark_node_type node_type) {
|
||||
return (node_type & CMARK_NODE_TYPE_MASK) == CMARK_NODE_TYPE_INLINE;
|
||||
}
|
||||
|
||||
static inline bool CMARK_NODE_INLINE_P(cmark_node *node) {
|
||||
return node != NULL && CMARK_NODE_TYPE_INLINE_P((cmark_node_type) node->type);
|
||||
}
|
||||
|
||||
CMARK_GFM_EXPORT bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type);
|
||||
|
||||
/**
|
||||
* Enable (or disable) extra safety checks. These extra checks cause
|
||||
* extra performance overhead (in some cases quadratic), so they are only
|
||||
* intended to be used during testing.
|
||||
*/
|
||||
CMARK_GFM_EXPORT void cmark_enable_safety_checks(bool enable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef CMARK_PARSER_H
|
||||
#define CMARK_PARSER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "references.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_LINK_LABEL_LENGTH 1000
|
||||
|
||||
struct cmark_parser {
|
||||
struct cmark_mem *mem;
|
||||
/* A hashtable of urls in the current document for cross-references */
|
||||
struct cmark_map *refmap;
|
||||
/* The root node of the parser, always a CMARK_NODE_DOCUMENT */
|
||||
struct cmark_node *root;
|
||||
/* The last open block after a line is fully processed */
|
||||
struct cmark_node *current;
|
||||
/* See the documentation for cmark_parser_get_line_number() in cmark.h */
|
||||
int line_number;
|
||||
/* See the documentation for cmark_parser_get_offset() in cmark.h */
|
||||
bufsize_t offset;
|
||||
/* See the documentation for cmark_parser_get_column() in cmark.h */
|
||||
bufsize_t column;
|
||||
/* See the documentation for cmark_parser_get_first_nonspace() in cmark.h */
|
||||
bufsize_t first_nonspace;
|
||||
/* See the documentation for cmark_parser_get_first_nonspace_column() in cmark.h */
|
||||
bufsize_t first_nonspace_column;
|
||||
bufsize_t thematic_break_kill_pos;
|
||||
/* See the documentation for cmark_parser_get_indent() in cmark.h */
|
||||
int indent;
|
||||
/* See the documentation for cmark_parser_is_blank() in cmark.h */
|
||||
bool blank;
|
||||
/* See the documentation for cmark_parser_has_partially_consumed_tab() in cmark.h */
|
||||
bool partially_consumed_tab;
|
||||
/* Contains the currently processed line */
|
||||
cmark_strbuf curline;
|
||||
/* See the documentation for cmark_parser_get_last_line_length() in cmark.h */
|
||||
bufsize_t last_line_length;
|
||||
/* FIXME: not sure about the difference with curline */
|
||||
cmark_strbuf linebuf;
|
||||
/* Options set by the user, see the Options section in cmark.h */
|
||||
int options;
|
||||
bool last_buffer_ended_with_cr;
|
||||
size_t total_size;
|
||||
cmark_llist *syntax_extensions;
|
||||
cmark_llist *inline_syntax_extensions;
|
||||
cmark_ispunct_func backslash_ispunct;
|
||||
/* used when parsing inlines, can be populated by extensions if any are loaded */
|
||||
int8_t *skip_chars;
|
||||
int8_t *special_chars;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef CMARK_PLUGIN_H
|
||||
#define CMARK_PLUGIN_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cmark_plugin:
|
||||
*
|
||||
* A plugin structure, which should be filled by plugin's
|
||||
* init functions.
|
||||
*/
|
||||
struct cmark_plugin {
|
||||
cmark_llist *syntax_extensions;
|
||||
};
|
||||
|
||||
cmark_llist *
|
||||
cmark_plugin_steal_syntax_extensions(cmark_plugin *plugin);
|
||||
|
||||
cmark_plugin *
|
||||
cmark_plugin_new(void);
|
||||
|
||||
void
|
||||
cmark_plugin_free(cmark_plugin *plugin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef CMARK_REFERENCES_H
|
||||
#define CMARK_REFERENCES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "map.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct cmark_reference {
|
||||
cmark_map_entry entry;
|
||||
bool is_attributes_reference;
|
||||
cmark_chunk url;
|
||||
cmark_chunk title;
|
||||
cmark_chunk attributes;
|
||||
};
|
||||
|
||||
typedef struct cmark_reference cmark_reference;
|
||||
|
||||
void cmark_reference_create(cmark_map *map, cmark_chunk *label,
|
||||
cmark_chunk *url, cmark_chunk *title);
|
||||
void cmark_reference_create_attributes(cmark_map *map, cmark_chunk *label,
|
||||
cmark_chunk *attributes);
|
||||
cmark_map *cmark_reference_map_new(cmark_mem *mem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef CMARK_REGISTRY_H
|
||||
#define CMARK_REGISTRY_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_register_plugin(cmark_plugin_init_func reg_fn);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_release_plugins(void);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
cmark_llist *cmark_list_syntax_extensions(cmark_mem *mem);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
#ifndef CMARK_RENDER_H
|
||||
#define CMARK_RENDER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum { LITERAL, NORMAL, TITLE, URL } cmark_escaping;
|
||||
|
||||
struct cmark_renderer {
|
||||
cmark_mem *mem;
|
||||
cmark_strbuf *buffer;
|
||||
cmark_strbuf *prefix;
|
||||
int column;
|
||||
int width;
|
||||
int need_cr;
|
||||
bufsize_t last_breakable;
|
||||
bool begin_line;
|
||||
bool begin_content;
|
||||
bool no_linebreaks;
|
||||
bool in_tight_list_item;
|
||||
void (*outc)(struct cmark_renderer *, cmark_node *, cmark_escaping, int32_t, unsigned char);
|
||||
void (*cr)(struct cmark_renderer *);
|
||||
void (*blankline)(struct cmark_renderer *);
|
||||
void (*out)(struct cmark_renderer *, cmark_node *, const char *, bool, cmark_escaping);
|
||||
unsigned int footnote_ix;
|
||||
};
|
||||
|
||||
typedef struct cmark_renderer cmark_renderer;
|
||||
|
||||
struct cmark_html_renderer {
|
||||
cmark_strbuf *html;
|
||||
cmark_node *plain;
|
||||
cmark_llist *filter_extensions;
|
||||
unsigned int footnote_ix;
|
||||
unsigned int written_footnote_ix;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
typedef struct cmark_html_renderer cmark_html_renderer;
|
||||
|
||||
void cmark_render_ascii(cmark_renderer *renderer, const char *s);
|
||||
|
||||
void cmark_render_code_point(cmark_renderer *renderer, uint32_t c);
|
||||
|
||||
char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
|
||||
void (*outc)(cmark_renderer *, cmark_node *,
|
||||
cmark_escaping, int32_t,
|
||||
unsigned char),
|
||||
int (*render_node)(cmark_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type, int options));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
#ifndef CMARK_SCANNERS_H
|
||||
#define CMARK_SCANNERS_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bufsize_t _scan_at(bufsize_t (*scanner)(const unsigned char *), cmark_chunk *c,
|
||||
bufsize_t offset);
|
||||
bufsize_t _scan_scheme(const unsigned char *p);
|
||||
bufsize_t _scan_autolink_uri(const unsigned char *p);
|
||||
bufsize_t _scan_autolink_email(const unsigned char *p);
|
||||
bufsize_t _scan_html_tag(const unsigned char *p);
|
||||
bufsize_t _scan_liberal_html_tag(const unsigned char *p);
|
||||
bufsize_t _scan_html_comment(const unsigned char *p);
|
||||
bufsize_t _scan_html_pi(const unsigned char *p);
|
||||
bufsize_t _scan_html_declaration(const unsigned char *p);
|
||||
bufsize_t _scan_html_cdata(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_start(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_start_7(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_1(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_2(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_3(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_4(const unsigned char *p);
|
||||
bufsize_t _scan_html_block_end_5(const unsigned char *p);
|
||||
bufsize_t _scan_link_title(const unsigned char *p);
|
||||
bufsize_t _scan_spacechars(const unsigned char *p);
|
||||
bufsize_t _scan_atx_heading_start(const unsigned char *p);
|
||||
bufsize_t _scan_setext_heading_line(const unsigned char *p);
|
||||
bufsize_t _scan_open_code_fence(const unsigned char *p);
|
||||
bufsize_t _scan_close_code_fence(const unsigned char *p);
|
||||
bufsize_t _scan_entity(const unsigned char *p);
|
||||
bufsize_t _scan_dangerous_url(const unsigned char *p);
|
||||
bufsize_t _scan_footnote_definition(const unsigned char *p);
|
||||
|
||||
#define scan_scheme(c, n) _scan_at(&_scan_scheme, c, n)
|
||||
#define scan_autolink_uri(c, n) _scan_at(&_scan_autolink_uri, c, n)
|
||||
#define scan_autolink_email(c, n) _scan_at(&_scan_autolink_email, c, n)
|
||||
#define scan_html_tag(c, n) _scan_at(&_scan_html_tag, c, n)
|
||||
#define scan_liberal_html_tag(c, n) _scan_at(&_scan_liberal_html_tag, c, n)
|
||||
#define scan_html_comment(c, n) _scan_at(&_scan_html_comment, c, n)
|
||||
#define scan_html_pi(c, n) _scan_at(&_scan_html_pi, c, n)
|
||||
#define scan_html_declaration(c, n) _scan_at(&_scan_html_declaration, c, n)
|
||||
#define scan_html_cdata(c, n) _scan_at(&_scan_html_cdata, c, n)
|
||||
#define scan_html_block_start(c, n) _scan_at(&_scan_html_block_start, c, n)
|
||||
#define scan_html_block_start_7(c, n) _scan_at(&_scan_html_block_start_7, c, n)
|
||||
#define scan_html_block_end_1(c, n) _scan_at(&_scan_html_block_end_1, c, n)
|
||||
#define scan_html_block_end_2(c, n) _scan_at(&_scan_html_block_end_2, c, n)
|
||||
#define scan_html_block_end_3(c, n) _scan_at(&_scan_html_block_end_3, c, n)
|
||||
#define scan_html_block_end_4(c, n) _scan_at(&_scan_html_block_end_4, c, n)
|
||||
#define scan_html_block_end_5(c, n) _scan_at(&_scan_html_block_end_5, c, n)
|
||||
#define scan_link_title(c, n) _scan_at(&_scan_link_title, c, n)
|
||||
#define scan_spacechars(c, n) _scan_at(&_scan_spacechars, c, n)
|
||||
#define scan_atx_heading_start(c, n) _scan_at(&_scan_atx_heading_start, c, n)
|
||||
#define scan_setext_heading_line(c, n) \
|
||||
_scan_at(&_scan_setext_heading_line, c, n)
|
||||
#define scan_open_code_fence(c, n) _scan_at(&_scan_open_code_fence, c, n)
|
||||
#define scan_close_code_fence(c, n) _scan_at(&_scan_close_code_fence, c, n)
|
||||
#define scan_entity(c, n) _scan_at(&_scan_entity, c, n)
|
||||
#define scan_dangerous_url(c, n) _scan_at(&_scan_dangerous_url, c, n)
|
||||
#define scan_footnote_definition(c, n) _scan_at(&_scan_footnote_definition, c, n)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef CMARK_SYNTAX_EXTENSION_H
|
||||
#define CMARK_SYNTAX_EXTENSION_H
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "cmark-gfm-extension_api.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct cmark_syntax_extension {
|
||||
cmark_match_block_func last_block_matches;
|
||||
cmark_open_block_func try_opening_block;
|
||||
cmark_match_inline_func match_inline;
|
||||
cmark_inline_from_delim_func insert_inline_from_delim;
|
||||
cmark_llist * special_inline_chars;
|
||||
char * name;
|
||||
void * priv;
|
||||
bool emphasis;
|
||||
cmark_free_func free_function;
|
||||
cmark_get_type_string_func get_type_string_func;
|
||||
cmark_can_contain_func can_contain_func;
|
||||
cmark_contains_inlines_func contains_inlines_func;
|
||||
cmark_common_render_func commonmark_render_func;
|
||||
cmark_common_render_func plaintext_render_func;
|
||||
cmark_common_render_func latex_render_func;
|
||||
cmark_xml_attr_func xml_attr_func;
|
||||
cmark_common_render_func man_render_func;
|
||||
cmark_html_render_func html_render_func;
|
||||
cmark_html_filter_func html_filter_func;
|
||||
cmark_postprocess_func postprocess_func;
|
||||
cmark_opaque_alloc_func opaque_alloc_func;
|
||||
cmark_opaque_free_func opaque_free_func;
|
||||
cmark_commonmark_escape_func commonmark_escape_func;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef CMARK_UTF8_H
|
||||
#define CMARK_UTF8_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
|
||||
bufsize_t len);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
|
||||
bufsize_t size);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_utf8proc_is_space(int32_t uc);
|
||||
|
||||
CMARK_GFM_EXPORT
|
||||
int cmark_utf8proc_is_punctuation(int32_t uc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,159 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "node.h"
|
||||
#include "cmark-gfm.h"
|
||||
#include "iterator.h"
|
||||
|
||||
cmark_iter *cmark_iter_new(cmark_node *root) {
|
||||
if (root == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
cmark_mem *mem = root->content.mem;
|
||||
cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter));
|
||||
iter->mem = mem;
|
||||
iter->root = root;
|
||||
iter->cur.ev_type = CMARK_EVENT_NONE;
|
||||
iter->cur.node = NULL;
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = root;
|
||||
return iter;
|
||||
}
|
||||
|
||||
void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); }
|
||||
|
||||
static bool S_is_leaf(cmark_node *node) {
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
cmark_event_type cmark_iter_next(cmark_iter *iter) {
|
||||
cmark_event_type ev_type = iter->next.ev_type;
|
||||
cmark_node *node = iter->next.node;
|
||||
|
||||
iter->cur.ev_type = ev_type;
|
||||
iter->cur.node = node;
|
||||
|
||||
if (ev_type == CMARK_EVENT_DONE) {
|
||||
return ev_type;
|
||||
}
|
||||
|
||||
/* roll forward to next item, setting both fields */
|
||||
if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) {
|
||||
if (node->first_child == NULL) {
|
||||
/* stay on this node but exit */
|
||||
iter->next.ev_type = CMARK_EVENT_EXIT;
|
||||
} else {
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = node->first_child;
|
||||
}
|
||||
} else if (node == iter->root) {
|
||||
/* don't move past root */
|
||||
iter->next.ev_type = CMARK_EVENT_DONE;
|
||||
iter->next.node = NULL;
|
||||
} else if (node->next) {
|
||||
iter->next.ev_type = CMARK_EVENT_ENTER;
|
||||
iter->next.node = node->next;
|
||||
} else if (node->parent) {
|
||||
iter->next.ev_type = CMARK_EVENT_EXIT;
|
||||
iter->next.node = node->parent;
|
||||
} else {
|
||||
assert(false);
|
||||
iter->next.ev_type = CMARK_EVENT_DONE;
|
||||
iter->next.node = NULL;
|
||||
}
|
||||
|
||||
return ev_type;
|
||||
}
|
||||
|
||||
void cmark_iter_reset(cmark_iter *iter, cmark_node *current,
|
||||
cmark_event_type event_type) {
|
||||
iter->next.ev_type = event_type;
|
||||
iter->next.node = current;
|
||||
cmark_iter_next(iter);
|
||||
}
|
||||
|
||||
cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; }
|
||||
|
||||
cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) {
|
||||
return iter->cur.ev_type;
|
||||
}
|
||||
|
||||
cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; }
|
||||
|
||||
void cmark_consolidate_text_nodes(cmark_node *root) {
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
cmark_strbuf buf = CMARK_BUF_INIT(iter->mem);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur, *tmp, *next;
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT &&
|
||||
cur->next && cur->next->type == CMARK_NODE_TEXT) {
|
||||
cmark_strbuf_clear(&buf);
|
||||
cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len);
|
||||
tmp = cur->next;
|
||||
while (tmp && tmp->type == CMARK_NODE_TEXT) {
|
||||
cmark_iter_next(iter); // advance pointer
|
||||
cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len);
|
||||
cur->end_column = tmp->end_column;
|
||||
next = tmp->next;
|
||||
cmark_node_free(tmp);
|
||||
tmp = next;
|
||||
}
|
||||
cmark_chunk_free(iter->mem, &cur->as.literal);
|
||||
cur->as.literal = cmark_chunk_buf_detach(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
cmark_strbuf_free(&buf);
|
||||
cmark_iter_free(iter);
|
||||
}
|
||||
|
||||
void cmark_node_own(cmark_node *root) {
|
||||
if (root == NULL) {
|
||||
return;
|
||||
}
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
cmark_event_type ev_type;
|
||||
cmark_node *cur;
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
if (ev_type == CMARK_EVENT_ENTER) {
|
||||
switch (cur->type) {
|
||||
case CMARK_NODE_TEXT:
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
case CMARK_NODE_CODE:
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
cmark_chunk_to_cstr(iter->mem, &cur->as.literal);
|
||||
break;
|
||||
case CMARK_NODE_LINK:
|
||||
cmark_chunk_to_cstr(iter->mem, &cur->as.link.url);
|
||||
cmark_chunk_to_cstr(iter->mem, &cur->as.link.title);
|
||||
break;
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
cmark_chunk_to_cstr(iter->mem, &cur->as.custom.on_enter);
|
||||
cmark_chunk_to_cstr(iter->mem, &cur->as.custom.on_exit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmark_iter_free(iter);
|
||||
}
|
||||
@@ -1,468 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "scanners.h"
|
||||
#include "render.h"
|
||||
#include "syntax_extension.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, node, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define LIST_NUMBER_STRING_SIZE 20
|
||||
|
||||
static inline void outc(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_escaping escape, int32_t c, unsigned char nextc) {
|
||||
if (escape == LITERAL) {
|
||||
cmark_render_code_point(renderer, c);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 123: // '{'
|
||||
case 125: // '}'
|
||||
case 35: // '#'
|
||||
case 37: // '%'
|
||||
case 38: // '&'
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
cmark_render_code_point(renderer, c);
|
||||
break;
|
||||
case 36: // '$'
|
||||
case 95: // '_'
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\\");
|
||||
}
|
||||
cmark_render_code_point(renderer, c);
|
||||
break;
|
||||
case 45: // '-'
|
||||
if (nextc == 45) { // prevent ligature
|
||||
cmark_render_ascii(renderer, "-{}");
|
||||
} else {
|
||||
cmark_render_ascii(renderer, "-");
|
||||
}
|
||||
break;
|
||||
case 126: // '~'
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\\textasciitilde{}");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 94: // '^'
|
||||
cmark_render_ascii(renderer, "\\^{}");
|
||||
break;
|
||||
case 92: // '\\'
|
||||
if (escape == URL) {
|
||||
// / acts as path sep even on windows:
|
||||
cmark_render_ascii(renderer, "/");
|
||||
} else {
|
||||
cmark_render_ascii(renderer, "\\textbackslash{}");
|
||||
}
|
||||
break;
|
||||
case 124: // '|'
|
||||
cmark_render_ascii(renderer, "\\textbar{}");
|
||||
break;
|
||||
case 60: // '<'
|
||||
cmark_render_ascii(renderer, "\\textless{}");
|
||||
break;
|
||||
case 62: // '>'
|
||||
cmark_render_ascii(renderer, "\\textgreater{}");
|
||||
break;
|
||||
case 91: // '['
|
||||
case 93: // ']'
|
||||
cmark_render_ascii(renderer, "{");
|
||||
cmark_render_code_point(renderer, c);
|
||||
cmark_render_ascii(renderer, "}");
|
||||
break;
|
||||
case 34: // '"'
|
||||
cmark_render_ascii(renderer, "\\textquotedbl{}");
|
||||
// requires \usepackage[T1]{fontenc}
|
||||
break;
|
||||
case 39: // '\''
|
||||
cmark_render_ascii(renderer, "\\textquotesingle{}");
|
||||
// requires \usepackage{textcomp}
|
||||
break;
|
||||
case 160: // nbsp
|
||||
cmark_render_ascii(renderer, "~");
|
||||
break;
|
||||
case 8230: // hellip
|
||||
cmark_render_ascii(renderer, "\\ldots{}");
|
||||
break;
|
||||
case 8216: // lsquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "`");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8217: // rsquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "\'");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8220: // ldquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "``");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8221: // rdquo
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "''");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8212: // emdash
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "---");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 8211: // endash
|
||||
if (escape == NORMAL) {
|
||||
cmark_render_ascii(renderer, "--");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
NO_LINK,
|
||||
URL_AUTOLINK,
|
||||
EMAIL_AUTOLINK,
|
||||
NORMAL_LINK,
|
||||
INTERNAL_LINK
|
||||
} link_type;
|
||||
|
||||
static link_type get_link_type(cmark_node *node) {
|
||||
size_t title_len, url_len;
|
||||
cmark_node *link_text;
|
||||
char *realurl;
|
||||
int realurllen;
|
||||
bool isemail = false;
|
||||
|
||||
if (node->type != CMARK_NODE_LINK) {
|
||||
return NO_LINK;
|
||||
}
|
||||
|
||||
const char *url = cmark_node_get_url(node);
|
||||
cmark_chunk url_chunk = cmark_chunk_literal(url);
|
||||
|
||||
if (url && *url == '#') {
|
||||
return INTERNAL_LINK;
|
||||
}
|
||||
|
||||
url_len = strlen(url);
|
||||
if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) {
|
||||
return NO_LINK;
|
||||
}
|
||||
|
||||
const char *title = cmark_node_get_title(node);
|
||||
title_len = strlen(title);
|
||||
// if it has a title, we can't treat it as an autolink:
|
||||
if (title_len == 0) {
|
||||
|
||||
link_text = node->first_child;
|
||||
cmark_consolidate_text_nodes(link_text);
|
||||
|
||||
if (!link_text)
|
||||
return NO_LINK;
|
||||
|
||||
realurl = (char *)url;
|
||||
realurllen = (int)url_len;
|
||||
if (strncmp(realurl, "mailto:", 7) == 0) {
|
||||
realurl += 7;
|
||||
realurllen -= 7;
|
||||
isemail = true;
|
||||
}
|
||||
if (realurllen == link_text->as.literal.len &&
|
||||
strncmp(realurl, (char *)link_text->as.literal.data,
|
||||
link_text->as.literal.len) == 0) {
|
||||
if (isemail) {
|
||||
return EMAIL_AUTOLINK;
|
||||
} else {
|
||||
return URL_AUTOLINK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NORMAL_LINK;
|
||||
}
|
||||
|
||||
static int S_get_enumlevel(cmark_node *node) {
|
||||
int enumlevel = 0;
|
||||
cmark_node *tmp = node;
|
||||
while (tmp) {
|
||||
if (tmp->type == CMARK_NODE_LIST &&
|
||||
cmark_node_get_list_type(node) == CMARK_ORDERED_LIST) {
|
||||
enumlevel++;
|
||||
}
|
||||
tmp = tmp->parent;
|
||||
}
|
||||
return enumlevel;
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
int list_number;
|
||||
int enumlevel;
|
||||
char list_number_string[LIST_NUMBER_STRING_SIZE];
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
cmark_list_type list_type;
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
|
||||
|
||||
if (node->extension && node->extension->latex_render_func) {
|
||||
node->extension->latex_render_func(node->extension, renderer, node, ev_type, options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
LIT("\\begin{quote}");
|
||||
CR();
|
||||
} else {
|
||||
LIT("\\end{quote}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
list_type = cmark_node_get_list_type(node);
|
||||
if (entering) {
|
||||
LIT("\\begin{");
|
||||
LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
|
||||
LIT("}");
|
||||
CR();
|
||||
list_number = cmark_node_get_list_start(node);
|
||||
if (list_number > 1) {
|
||||
enumlevel = S_get_enumlevel(node);
|
||||
// latex normally supports only five levels
|
||||
if (enumlevel >= 1 && enumlevel <= 5) {
|
||||
snprintf(list_number_string, LIST_NUMBER_STRING_SIZE, "%d",
|
||||
list_number);
|
||||
LIT("\\setcounter{enum");
|
||||
switch (enumlevel) {
|
||||
case 1: LIT("i"); break;
|
||||
case 2: LIT("ii"); break;
|
||||
case 3: LIT("iii"); break;
|
||||
case 4: LIT("iv"); break;
|
||||
case 5: LIT("v"); break;
|
||||
default: LIT("i"); break;
|
||||
}
|
||||
LIT("}{");
|
||||
OUT(list_number_string, false, NORMAL);
|
||||
LIT("}");
|
||||
}
|
||||
CR();
|
||||
}
|
||||
} else {
|
||||
LIT("\\end{");
|
||||
LIT(list_type == CMARK_ORDERED_LIST ? "enumerate" : "itemize");
|
||||
LIT("}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
LIT("\\item ");
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
switch (cmark_node_get_heading_level(node)) {
|
||||
case 1:
|
||||
LIT("\\section");
|
||||
break;
|
||||
case 2:
|
||||
LIT("\\subsection");
|
||||
break;
|
||||
case 3:
|
||||
LIT("\\subsubsection");
|
||||
break;
|
||||
case 4:
|
||||
LIT("\\paragraph");
|
||||
break;
|
||||
case 5:
|
||||
LIT("\\subparagraph");
|
||||
break;
|
||||
}
|
||||
LIT("{");
|
||||
} else {
|
||||
LIT("}");
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
CR();
|
||||
LIT("\\begin{verbatim}");
|
||||
CR();
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
CR();
|
||||
LIT("\\end{verbatim}");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
CR();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
BLANKLINE();
|
||||
LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}");
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (!entering) {
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
LIT("\\\\");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
LIT("\\\\");
|
||||
CR();
|
||||
} else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
LIT("\\texttt{");
|
||||
OUT(cmark_node_get_literal(node), false, NORMAL);
|
||||
LIT("}");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
||||
if (entering) {
|
||||
LIT("\\textbf{");
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
LIT("\\emph{");
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (entering) {
|
||||
const char *url = cmark_node_get_url(node);
|
||||
// requires \usepackage{hyperref}
|
||||
switch (get_link_type(node)) {
|
||||
case URL_AUTOLINK:
|
||||
LIT("\\url{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}");
|
||||
return 0; // Don't process further nodes to avoid double-rendering artefacts
|
||||
case EMAIL_AUTOLINK:
|
||||
LIT("\\href{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}\\nolinkurl{");
|
||||
break;
|
||||
case NORMAL_LINK:
|
||||
LIT("\\href{");
|
||||
OUT(url, false, URL);
|
||||
LIT("}{");
|
||||
break;
|
||||
case INTERNAL_LINK:
|
||||
LIT("\\protect\\hyperlink{");
|
||||
OUT(url + 1, false, URL);
|
||||
LIT("}{");
|
||||
break;
|
||||
case NO_LINK:
|
||||
LIT("{"); // error?
|
||||
}
|
||||
} else {
|
||||
LIT("}");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT("\\protect\\includegraphics{");
|
||||
// requires \include{graphicx}
|
||||
OUT(cmark_node_get_url(node), false, URL);
|
||||
LIT("}");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
||||
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
||||
case CMARK_NODE_ATTRIBUTE:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_latex(cmark_node *root, int options, int width) {
|
||||
return cmark_render_latex_with_mem(root, options, width, cmark_node_mem(root));
|
||||
}
|
||||
|
||||
char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) {
|
||||
return cmark_render(mem, root, options, width, outc, S_render_node);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
|
||||
cmark_llist *cmark_llist_append(cmark_mem *mem, cmark_llist *head, void *data) {
|
||||
cmark_llist *tmp;
|
||||
cmark_llist *new_node = (cmark_llist *) mem->calloc(1, sizeof(cmark_llist));
|
||||
|
||||
new_node->data = data;
|
||||
new_node->next = NULL;
|
||||
|
||||
if (!head)
|
||||
return new_node;
|
||||
|
||||
for (tmp = head; tmp->next; tmp=tmp->next);
|
||||
|
||||
tmp->next = new_node;
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
void cmark_llist_free_full(cmark_mem *mem, cmark_llist *head, cmark_free_func free_func) {
|
||||
cmark_llist *tmp, *prev;
|
||||
|
||||
for (tmp = head; tmp;) {
|
||||
if (free_func)
|
||||
free_func(mem, tmp->data);
|
||||
|
||||
prev = tmp;
|
||||
tmp = tmp->next;
|
||||
mem->free(prev);
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_llist_free(cmark_mem *mem, cmark_llist *head) {
|
||||
cmark_llist_free_full(mem, head, NULL);
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "node.h"
|
||||
#include "buffer.h"
|
||||
#include "utf8.h"
|
||||
#include "render.h"
|
||||
#include "syntax_extension.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, node, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define LIST_NUMBER_SIZE 20
|
||||
|
||||
// Functions to convert cmark_nodes to groff man strings.
|
||||
static void S_outc(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_escaping escape, int32_t c,
|
||||
unsigned char nextc) {
|
||||
(void)(nextc);
|
||||
|
||||
if (escape == LITERAL) {
|
||||
cmark_render_code_point(renderer, c);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 46:
|
||||
if (renderer->begin_line) {
|
||||
cmark_render_ascii(renderer, "\\&.");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 39:
|
||||
if (renderer->begin_line) {
|
||||
cmark_render_ascii(renderer, "\\&'");
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
break;
|
||||
case 45:
|
||||
cmark_render_ascii(renderer, "\\-");
|
||||
break;
|
||||
case 92:
|
||||
cmark_render_ascii(renderer, "\\e");
|
||||
break;
|
||||
case 8216: // left single quote
|
||||
cmark_render_ascii(renderer, "\\[oq]");
|
||||
break;
|
||||
case 8217: // right single quote
|
||||
cmark_render_ascii(renderer, "\\[cq]");
|
||||
break;
|
||||
case 8220: // left double quote
|
||||
cmark_render_ascii(renderer, "\\[lq]");
|
||||
break;
|
||||
case 8221: // right double quote
|
||||
cmark_render_ascii(renderer, "\\[rq]");
|
||||
break;
|
||||
case 8212: // em dash
|
||||
cmark_render_ascii(renderer, "\\[em]");
|
||||
break;
|
||||
case 8211: // en dash
|
||||
cmark_render_ascii(renderer, "\\[en]");
|
||||
break;
|
||||
default:
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
int list_number;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
|
||||
|
||||
if (node->extension && node->extension->man_render_func) {
|
||||
node->extension->man_render_func(node->extension, renderer, node, ev_type, options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
if (entering) {
|
||||
/* Define a strikethrough macro */
|
||||
/* Commenting out because this makes tests fail
|
||||
LIT(".de ST");
|
||||
CR();
|
||||
LIT(".nr ww \\w'\\\\$1'");
|
||||
CR();
|
||||
LIT("\\Z@\\v'-.25m'\\l'\\\\n[ww]u'@\\\\$1");
|
||||
CR();
|
||||
LIT("..");
|
||||
CR();
|
||||
*/
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
if (entering) {
|
||||
CR();
|
||||
LIT(".RS");
|
||||
CR();
|
||||
} else {
|
||||
CR();
|
||||
LIT(".RE");
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (entering) {
|
||||
CR();
|
||||
LIT(".IP ");
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
LIT("\\[bu] 2");
|
||||
} else {
|
||||
list_number = cmark_node_get_item_index(node);
|
||||
char list_number_s[LIST_NUMBER_SIZE];
|
||||
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
|
||||
LIT(list_number_s);
|
||||
}
|
||||
CR();
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
CR();
|
||||
LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS");
|
||||
CR();
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
CR();
|
||||
LIT(".IP\n.nf\n\\f[C]\n");
|
||||
OUT(cmark_node_get_literal(node), false, NORMAL);
|
||||
CR();
|
||||
LIT("\\f[]\n.fi");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
CR();
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
CR();
|
||||
LIT(".PP\n * * * * *");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (entering) {
|
||||
// no blank line if first paragraph in list:
|
||||
if (node->parent && node->parent->type == CMARK_NODE_ITEM &&
|
||||
node->prev == NULL) {
|
||||
// no blank line or .PP
|
||||
} else {
|
||||
CR();
|
||||
LIT(".PP");
|
||||
CR();
|
||||
}
|
||||
} else {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
LIT(".PD 0\n.P\n.PD");
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
LIT(".PD 0\n.P\n.PD");
|
||||
CR();
|
||||
} else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, LITERAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
LIT("\\f[C]");
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
LIT("\\f[]");
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
|
||||
false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
||||
if (entering) {
|
||||
LIT("\\f[B]");
|
||||
} else {
|
||||
LIT("\\f[]");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
if (entering) {
|
||||
LIT("\\f[I]");
|
||||
} else {
|
||||
LIT("\\f[]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
if (!entering) {
|
||||
LIT(" (");
|
||||
OUT(cmark_node_get_url(node), allow_wrap, URL);
|
||||
LIT(")");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
if (entering) {
|
||||
LIT("[IMAGE: ");
|
||||
} else {
|
||||
LIT("]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ATTRIBUTE:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
||||
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_man(cmark_node *root, int options, int width) {
|
||||
return cmark_render_man_with_mem(root, options, width, cmark_node_mem(root));
|
||||
}
|
||||
|
||||
char *cmark_render_man_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) {
|
||||
return cmark_render(mem, root, options, width, S_outc, S_render_node);
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
#include "map.h"
|
||||
#include "utf8.h"
|
||||
#include "parser.h"
|
||||
|
||||
// normalize map label: collapse internal whitespace to single space,
|
||||
// remove leading/trailing whitespace, case fold
|
||||
// Return NULL if the label is actually empty (i.e. composed solely from
|
||||
// whitespace)
|
||||
unsigned char *normalize_map_label(cmark_mem *mem, cmark_chunk *ref) {
|
||||
cmark_strbuf normalized = CMARK_BUF_INIT(mem);
|
||||
unsigned char *result;
|
||||
|
||||
if (ref == NULL)
|
||||
return NULL;
|
||||
|
||||
if (ref->len == 0)
|
||||
return NULL;
|
||||
|
||||
cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
|
||||
cmark_strbuf_trim(&normalized);
|
||||
cmark_strbuf_normalize_whitespace(&normalized);
|
||||
|
||||
result = cmark_strbuf_detach(&normalized);
|
||||
assert(result);
|
||||
|
||||
if (result[0] == '\0') {
|
||||
mem->free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
labelcmp(const unsigned char *a, const unsigned char *b) {
|
||||
return strcmp((const char *)a, (const char *)b);
|
||||
}
|
||||
|
||||
static int
|
||||
refcmp(const void *p1, const void *p2) {
|
||||
cmark_map_entry *r1 = *(cmark_map_entry **)p1;
|
||||
cmark_map_entry *r2 = *(cmark_map_entry **)p2;
|
||||
int res = labelcmp(r1->label, r2->label);
|
||||
return res ? res : ((int)r1->age - (int)r2->age);
|
||||
}
|
||||
|
||||
static int
|
||||
refsearch(const void *label, const void *p2) {
|
||||
cmark_map_entry *ref = *(cmark_map_entry **)p2;
|
||||
return labelcmp((const unsigned char *)label, ref->label);
|
||||
}
|
||||
|
||||
static void sort_map(cmark_map *map) {
|
||||
size_t i = 0, last = 0, size = map->size;
|
||||
cmark_map_entry *r = map->refs, **sorted = NULL;
|
||||
|
||||
sorted = (cmark_map_entry **)map->mem->calloc(size, sizeof(cmark_map_entry *));
|
||||
while (r) {
|
||||
sorted[i++] = r;
|
||||
r = r->next;
|
||||
}
|
||||
|
||||
qsort(sorted, size, sizeof(cmark_map_entry *), refcmp);
|
||||
|
||||
for (i = 1; i < size; i++) {
|
||||
if (labelcmp(sorted[i]->label, sorted[last]->label) != 0)
|
||||
sorted[++last] = sorted[i];
|
||||
}
|
||||
|
||||
map->sorted = sorted;
|
||||
map->size = last + 1;
|
||||
}
|
||||
|
||||
cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) {
|
||||
cmark_map_entry **ref = NULL;
|
||||
cmark_map_entry *r = NULL;
|
||||
unsigned char *norm;
|
||||
|
||||
if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
|
||||
return NULL;
|
||||
|
||||
if (map == NULL || !map->size)
|
||||
return NULL;
|
||||
|
||||
norm = normalize_map_label(map->mem, label);
|
||||
if (norm == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!map->sorted)
|
||||
sort_map(map);
|
||||
|
||||
ref = (cmark_map_entry **)bsearch(norm, map->sorted, map->size, sizeof(cmark_map_entry *), refsearch);
|
||||
map->mem->free(norm);
|
||||
|
||||
if (ref != NULL) {
|
||||
r = ref[0];
|
||||
/* Check for expansion limit */
|
||||
if (r->size > map->max_ref_size - map->ref_size)
|
||||
return NULL;
|
||||
map->ref_size += r->size;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void cmark_map_free(cmark_map *map) {
|
||||
cmark_map_entry *ref;
|
||||
|
||||
if (map == NULL)
|
||||
return;
|
||||
|
||||
ref = map->refs;
|
||||
while (ref) {
|
||||
cmark_map_entry *next = ref->next;
|
||||
map->free(map, ref);
|
||||
ref = next;
|
||||
}
|
||||
|
||||
map->mem->free(map->sorted);
|
||||
map->mem->free(map);
|
||||
}
|
||||
|
||||
cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free) {
|
||||
cmark_map *map = (cmark_map *)mem->calloc(1, sizeof(cmark_map));
|
||||
map->mem = mem;
|
||||
map->free = free;
|
||||
map->max_ref_size = UINT_MAX;
|
||||
return map;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,223 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "node.h"
|
||||
#include "syntax_extension.h"
|
||||
#include "render.h"
|
||||
|
||||
#define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping)
|
||||
#define LIT(s) renderer->out(renderer, node, s, false, LITERAL)
|
||||
#define CR() renderer->cr(renderer)
|
||||
#define BLANKLINE() renderer->blankline(renderer)
|
||||
#define LISTMARKER_SIZE 20
|
||||
|
||||
// Functions to convert cmark_nodes to plain text strings.
|
||||
|
||||
static inline void outc(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_escaping escape, int32_t c, unsigned char nextc) {
|
||||
cmark_render_code_point(renderer, c);
|
||||
}
|
||||
|
||||
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
||||
cmark_event_type ev_type, int options) {
|
||||
int list_number;
|
||||
cmark_delim_type list_delim;
|
||||
int i;
|
||||
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
||||
char listmarker[LISTMARKER_SIZE];
|
||||
bool first_in_list_item;
|
||||
bufsize_t marker_width;
|
||||
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
|
||||
!(CMARK_OPT_HARDBREAKS & options);
|
||||
|
||||
// Don't adjust tight list status til we've started the list.
|
||||
// Otherwise we loose the blank line between a paragraph and
|
||||
// a following list.
|
||||
if (entering) {
|
||||
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
|
||||
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
|
||||
}
|
||||
} else {
|
||||
if (node->type == CMARK_NODE_LIST) {
|
||||
renderer->in_tight_list_item =
|
||||
node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM &&
|
||||
node->parent->parent->as.list.tight;
|
||||
}
|
||||
}
|
||||
|
||||
if (node->extension && node->extension->plaintext_render_func) {
|
||||
node->extension->plaintext_render_func(node->extension, renderer, node, ev_type, options);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMARK_NODE_DOCUMENT:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_BLOCK_QUOTE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LIST:
|
||||
if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK ||
|
||||
node->next->type == CMARK_NODE_LIST)) {
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ITEM:
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
marker_width = 4;
|
||||
} else {
|
||||
list_number = cmark_node_get_item_index(node);
|
||||
list_delim = cmark_node_get_list_delim(node->parent);
|
||||
// we ensure a width of at least 4 so
|
||||
// we get nice transition from single digits
|
||||
// to double
|
||||
snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number,
|
||||
list_delim == CMARK_PAREN_DELIM ? ")" : ".",
|
||||
list_number < 10 ? " " : " ");
|
||||
marker_width = (bufsize_t)strlen(listmarker);
|
||||
}
|
||||
if (entering) {
|
||||
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
||||
LIT(" - ");
|
||||
renderer->begin_content = true;
|
||||
} else {
|
||||
LIT(listmarker);
|
||||
renderer->begin_content = true;
|
||||
}
|
||||
for (i = marker_width; i--;) {
|
||||
cmark_strbuf_putc(renderer->prefix, ' ');
|
||||
}
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix,
|
||||
renderer->prefix->size - marker_width);
|
||||
CR();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HEADING:
|
||||
if (entering) {
|
||||
renderer->begin_content = true;
|
||||
renderer->no_linebreaks = true;
|
||||
} else {
|
||||
renderer->no_linebreaks = false;
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE_BLOCK:
|
||||
first_in_list_item = node->prev == NULL && node->parent &&
|
||||
node->parent->type == CMARK_NODE_ITEM;
|
||||
|
||||
if (!first_in_list_item) {
|
||||
BLANKLINE();
|
||||
}
|
||||
OUT(cmark_node_get_literal(node), false, LITERAL);
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_BLOCK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_THEMATIC_BREAK:
|
||||
BLANKLINE();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_PARAGRAPH:
|
||||
if (!entering) {
|
||||
BLANKLINE();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_TEXT:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINEBREAK:
|
||||
CR();
|
||||
break;
|
||||
|
||||
case CMARK_NODE_SOFTBREAK:
|
||||
if (CMARK_OPT_HARDBREAKS & options) {
|
||||
CR();
|
||||
} else if (!renderer->no_linebreaks && renderer->width == 0 &&
|
||||
!(CMARK_OPT_HARDBREAKS & options) &&
|
||||
!(CMARK_OPT_NOBREAKS & options)) {
|
||||
CR();
|
||||
} else {
|
||||
OUT(" ", allow_wrap, LITERAL);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CODE:
|
||||
OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_HTML_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_CUSTOM_INLINE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_STRONG:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_EMPH:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_LINK:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_IMAGE:
|
||||
break;
|
||||
|
||||
case CMARK_NODE_ATTRIBUTE:
|
||||
OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
||||
if (entering) {
|
||||
LIT("[^");
|
||||
OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
|
||||
LIT("]");
|
||||
}
|
||||
break;
|
||||
|
||||
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
||||
if (entering) {
|
||||
renderer->footnote_ix += 1;
|
||||
LIT("[^");
|
||||
char n[32];
|
||||
snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
|
||||
OUT(n, false, LITERAL);
|
||||
LIT("]: ");
|
||||
|
||||
cmark_strbuf_puts(renderer->prefix, " ");
|
||||
} else {
|
||||
cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *cmark_render_plaintext(cmark_node *root, int options, int width) {
|
||||
return cmark_render_plaintext_with_mem(root, options, width, cmark_node_mem(root));
|
||||
}
|
||||
|
||||
char *cmark_render_plaintext_with_mem(cmark_node *root, int options, int width, cmark_mem *mem) {
|
||||
if (options & CMARK_OPT_HARDBREAKS) {
|
||||
// disable breaking on width, since it has
|
||||
// a different meaning with OPT_HARDBREAKS
|
||||
width = 0;
|
||||
}
|
||||
return cmark_render(mem, root, options, width, outc, S_render_node);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "plugin.h"
|
||||
|
||||
extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR;
|
||||
|
||||
int cmark_plugin_register_syntax_extension(cmark_plugin * plugin,
|
||||
cmark_syntax_extension * extension) {
|
||||
plugin->syntax_extensions = cmark_llist_append(&CMARK_DEFAULT_MEM_ALLOCATOR, plugin->syntax_extensions, extension);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cmark_plugin *
|
||||
cmark_plugin_new(void) {
|
||||
cmark_plugin *res = (cmark_plugin *) CMARK_DEFAULT_MEM_ALLOCATOR.calloc(1, sizeof(cmark_plugin));
|
||||
|
||||
res->syntax_extensions = NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
cmark_plugin_free(cmark_plugin *plugin) {
|
||||
cmark_llist_free_full(&CMARK_DEFAULT_MEM_ALLOCATOR,
|
||||
plugin->syntax_extensions,
|
||||
(cmark_free_func) cmark_syntax_extension_free);
|
||||
CMARK_DEFAULT_MEM_ALLOCATOR.free(plugin);
|
||||
}
|
||||
|
||||
cmark_llist *
|
||||
cmark_plugin_steal_syntax_extensions(cmark_plugin *plugin) {
|
||||
cmark_llist *res = plugin->syntax_extensions;
|
||||
|
||||
plugin->syntax_extensions = NULL;
|
||||
return res;
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "parser.h"
|
||||
#include "references.h"
|
||||
#include "inlines.h"
|
||||
#include "chunk.h"
|
||||
|
||||
static void reference_free(cmark_map *map, cmark_map_entry *_ref) {
|
||||
cmark_reference *ref = (cmark_reference *)_ref;
|
||||
cmark_mem *mem = map->mem;
|
||||
if (ref != NULL) {
|
||||
mem->free(ref->entry.label);
|
||||
cmark_chunk_free(mem, &ref->url);
|
||||
cmark_chunk_free(mem, &ref->title);
|
||||
cmark_chunk_free(mem, &ref->attributes);
|
||||
mem->free(ref);
|
||||
}
|
||||
}
|
||||
|
||||
void cmark_reference_create(cmark_map *map, cmark_chunk *label,
|
||||
cmark_chunk *url, cmark_chunk *title) {
|
||||
cmark_reference *ref;
|
||||
unsigned char *reflabel = normalize_map_label(map->mem, label);
|
||||
|
||||
/* empty reference name, or composed from only whitespace */
|
||||
if (reflabel == NULL)
|
||||
return;
|
||||
|
||||
assert(map->sorted == NULL);
|
||||
|
||||
ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
|
||||
ref->entry.label = reflabel;
|
||||
ref->is_attributes_reference = false;
|
||||
ref->url = cmark_clean_url(map->mem, url);
|
||||
ref->title = cmark_clean_title(map->mem, title);
|
||||
ref->attributes = cmark_chunk_literal("");
|
||||
ref->entry.age = map->size;
|
||||
ref->entry.next = map->refs;
|
||||
ref->entry.size = ref->url.len + ref->title.len;
|
||||
|
||||
map->refs = (cmark_map_entry *)ref;
|
||||
map->size++;
|
||||
}
|
||||
|
||||
void cmark_reference_create_attributes(cmark_map *map, cmark_chunk *label,
|
||||
cmark_chunk *attributes) {
|
||||
cmark_reference *ref;
|
||||
unsigned char *reflabel = normalize_map_label(map->mem, label);
|
||||
|
||||
/* empty reference name, or composed from only whitespace */
|
||||
if (reflabel == NULL)
|
||||
return;
|
||||
|
||||
assert(map->sorted == NULL);
|
||||
|
||||
ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
|
||||
ref->entry.label = reflabel;
|
||||
ref->is_attributes_reference = true;
|
||||
ref->url = cmark_chunk_literal("");
|
||||
ref->title = cmark_chunk_literal("");
|
||||
ref->attributes = cmark_clean_attributes(map->mem, attributes);
|
||||
ref->entry.age = map->size;
|
||||
ref->entry.next = map->refs;
|
||||
|
||||
map->refs = (cmark_map_entry *)ref;
|
||||
map->size++;
|
||||
}
|
||||
|
||||
cmark_map *cmark_reference_map_new(cmark_mem *mem) {
|
||||
return cmark_map_new(mem, reference_free);
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "mutex.h"
|
||||
#include "syntax_extension.h"
|
||||
#include "registry.h"
|
||||
#include "plugin.h"
|
||||
|
||||
extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR;
|
||||
|
||||
static cmark_llist *syntax_extensions = NULL;
|
||||
|
||||
CMARK_DEFINE_LOCK(extensions);
|
||||
|
||||
void cmark_register_plugin(cmark_plugin_init_func reg_fn) {
|
||||
cmark_plugin *plugin = cmark_plugin_new();
|
||||
|
||||
if (!reg_fn(plugin)) {
|
||||
cmark_plugin_free(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
cmark_llist *syntax_extensions_list = cmark_plugin_steal_syntax_extensions(plugin),
|
||||
*it;
|
||||
|
||||
CMARK_INITIALIZE_AND_LOCK(extensions);
|
||||
|
||||
for (it = syntax_extensions_list; it; it = it->next) {
|
||||
syntax_extensions = cmark_llist_append(&CMARK_DEFAULT_MEM_ALLOCATOR, syntax_extensions, it->data);
|
||||
}
|
||||
|
||||
CMARK_UNLOCK(extensions);
|
||||
|
||||
cmark_llist_free(&CMARK_DEFAULT_MEM_ALLOCATOR, syntax_extensions_list);
|
||||
cmark_plugin_free(plugin);
|
||||
}
|
||||
|
||||
void cmark_release_plugins(void) {
|
||||
CMARK_INITIALIZE_AND_LOCK(extensions);
|
||||
|
||||
if (syntax_extensions) {
|
||||
cmark_llist_free_full(
|
||||
&CMARK_DEFAULT_MEM_ALLOCATOR,
|
||||
syntax_extensions,
|
||||
(cmark_free_func) cmark_syntax_extension_free);
|
||||
syntax_extensions = NULL;
|
||||
}
|
||||
|
||||
CMARK_UNLOCK(extensions);
|
||||
}
|
||||
|
||||
cmark_llist *cmark_list_syntax_extensions(cmark_mem *mem) {
|
||||
cmark_llist *it;
|
||||
cmark_llist *res = NULL;
|
||||
|
||||
CMARK_INITIALIZE_AND_LOCK(extensions);
|
||||
|
||||
for (it = syntax_extensions; it; it = it->next) {
|
||||
res = cmark_llist_append(mem, res, it->data);
|
||||
}
|
||||
|
||||
CMARK_UNLOCK(extensions);
|
||||
return res;
|
||||
}
|
||||
|
||||
cmark_syntax_extension *cmark_find_syntax_extension(const char *name) {
|
||||
cmark_llist *tmp;
|
||||
cmark_syntax_extension *res = NULL;
|
||||
|
||||
CMARK_INITIALIZE_AND_LOCK(extensions);
|
||||
|
||||
for (tmp = syntax_extensions; tmp; tmp = tmp->next) {
|
||||
cmark_syntax_extension *ext = (cmark_syntax_extension *) tmp->data;
|
||||
if (!strcmp(ext->name, name)) {
|
||||
res = ext;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CMARK_UNLOCK(extensions);
|
||||
return res;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "chunk.h"
|
||||
#include "cmark-gfm.h"
|
||||
#include "utf8.h"
|
||||
#include "render.h"
|
||||
#include "node.h"
|
||||
#include "syntax_extension.h"
|
||||
|
||||
static inline void S_cr(cmark_renderer *renderer) {
|
||||
if (renderer->need_cr < 1) {
|
||||
renderer->need_cr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void S_blankline(cmark_renderer *renderer) {
|
||||
if (renderer->need_cr < 2) {
|
||||
renderer->need_cr = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void S_out(cmark_renderer *renderer, cmark_node *node,
|
||||
const char *source, bool wrap,
|
||||
cmark_escaping escape) {
|
||||
int length = (int)strlen(source);
|
||||
unsigned char nextc;
|
||||
int32_t c;
|
||||
int i = 0;
|
||||
int last_nonspace;
|
||||
int len;
|
||||
cmark_chunk remainder = cmark_chunk_literal("");
|
||||
int k = renderer->buffer->size - 1;
|
||||
|
||||
cmark_syntax_extension *ext = node->ancestor_extension;
|
||||
if (ext && !ext->commonmark_escape_func)
|
||||
ext = NULL;
|
||||
|
||||
wrap = wrap && !renderer->no_linebreaks;
|
||||
|
||||
if (renderer->in_tight_list_item && renderer->need_cr > 1) {
|
||||
renderer->need_cr = 1;
|
||||
}
|
||||
while (renderer->need_cr) {
|
||||
if (k < 0 || renderer->buffer->ptr[k] == '\n') {
|
||||
k -= 1;
|
||||
} else {
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
if (renderer->need_cr > 1) {
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
}
|
||||
}
|
||||
renderer->column = 0;
|
||||
renderer->last_breakable = 0;
|
||||
renderer->begin_line = true;
|
||||
renderer->begin_content = true;
|
||||
renderer->need_cr -= 1;
|
||||
}
|
||||
|
||||
while (i < length) {
|
||||
if (renderer->begin_line) {
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
// note: this assumes prefix is ascii:
|
||||
renderer->column = renderer->prefix->size;
|
||||
}
|
||||
|
||||
len = cmark_utf8proc_iterate((const uint8_t *)source + i, length - i, &c);
|
||||
if (len == -1) { // error condition
|
||||
return; // return without rendering rest of string
|
||||
}
|
||||
|
||||
if (ext && ext->commonmark_escape_func(ext, node, c))
|
||||
cmark_strbuf_putc(renderer->buffer, '\\');
|
||||
|
||||
nextc = source[i + len];
|
||||
if (c == 32 && wrap) {
|
||||
if (!renderer->begin_line) {
|
||||
last_nonspace = renderer->buffer->size;
|
||||
cmark_strbuf_putc(renderer->buffer, ' ');
|
||||
renderer->column += 1;
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content = false;
|
||||
// skip following spaces
|
||||
while (source[i + 1] == ' ') {
|
||||
i++;
|
||||
}
|
||||
// We don't allow breaks that make a digit the first character
|
||||
// because this causes problems with commonmark output.
|
||||
if (!cmark_isdigit(source[i + 1])) {
|
||||
renderer->last_breakable = last_nonspace;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (escape == LITERAL) {
|
||||
if (c == 10) {
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
renderer->column = 0;
|
||||
renderer->begin_line = true;
|
||||
renderer->begin_content = true;
|
||||
renderer->last_breakable = 0;
|
||||
} else {
|
||||
cmark_render_code_point(renderer, c);
|
||||
renderer->begin_line = false;
|
||||
// we don't set 'begin_content' to false til we've
|
||||
// finished parsing a digit. Reason: in commonmark
|
||||
// we need to escape a potential list marker after
|
||||
// a digit:
|
||||
renderer->begin_content =
|
||||
renderer->begin_content && cmark_isdigit((char)c) == 1;
|
||||
}
|
||||
} else {
|
||||
(renderer->outc)(renderer, node, escape, c, nextc);
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content =
|
||||
renderer->begin_content && cmark_isdigit((char)c) == 1;
|
||||
}
|
||||
|
||||
// If adding the character went beyond width, look for an
|
||||
// earlier place where the line could be broken:
|
||||
if (renderer->width > 0 && renderer->column > renderer->width &&
|
||||
!renderer->begin_line && renderer->last_breakable > 0) {
|
||||
|
||||
// copy from last_breakable to remainder
|
||||
cmark_chunk_set_cstr(renderer->mem, &remainder,
|
||||
(char *)renderer->buffer->ptr +
|
||||
renderer->last_breakable + 1);
|
||||
// truncate at last_breakable
|
||||
cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable);
|
||||
// add newline, prefix, and remainder
|
||||
cmark_strbuf_putc(renderer->buffer, '\n');
|
||||
cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
|
||||
renderer->prefix->size);
|
||||
cmark_strbuf_put(renderer->buffer, remainder.data, remainder.len);
|
||||
renderer->column = renderer->prefix->size + remainder.len;
|
||||
cmark_chunk_free(renderer->mem, &remainder);
|
||||
renderer->last_breakable = 0;
|
||||
renderer->begin_line = false;
|
||||
renderer->begin_content = false;
|
||||
}
|
||||
|
||||
i += len;
|
||||
}
|
||||
}
|
||||
|
||||
// Assumes no newlines, assumes ascii content:
|
||||
void cmark_render_ascii(cmark_renderer *renderer, const char *s) {
|
||||
int origsize = renderer->buffer->size;
|
||||
cmark_strbuf_puts(renderer->buffer, s);
|
||||
renderer->column += renderer->buffer->size - origsize;
|
||||
}
|
||||
|
||||
void cmark_render_code_point(cmark_renderer *renderer, uint32_t c) {
|
||||
cmark_utf8proc_encode_char(c, renderer->buffer);
|
||||
renderer->column += 1;
|
||||
}
|
||||
|
||||
char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
|
||||
void (*outc)(cmark_renderer *, cmark_node *,
|
||||
cmark_escaping, int32_t,
|
||||
unsigned char),
|
||||
int (*render_node)(cmark_renderer *renderer,
|
||||
cmark_node *node,
|
||||
cmark_event_type ev_type, int options)) {
|
||||
cmark_strbuf pref = CMARK_BUF_INIT(mem);
|
||||
cmark_strbuf buf = CMARK_BUF_INIT(mem);
|
||||
cmark_node *cur;
|
||||
cmark_event_type ev_type;
|
||||
char *result;
|
||||
cmark_iter *iter = cmark_iter_new(root);
|
||||
|
||||
cmark_renderer renderer = {mem, &buf, &pref, 0, width,
|
||||
0, 0, true, true, false,
|
||||
false, outc, S_cr, S_blankline, S_out,
|
||||
0};
|
||||
|
||||
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
||||
cur = cmark_iter_get_node(iter);
|
||||
if (cur->extension) {
|
||||
cur->ancestor_extension = cur->extension;
|
||||
} else if (cur->parent) {
|
||||
cur->ancestor_extension = cur->parent->ancestor_extension;
|
||||
}
|
||||
if (cur->type == CMARK_NODE_ITEM) {
|
||||
// Calculate the list item's index, for the benefit of output formats
|
||||
// like commonmark and plaintext.
|
||||
if (cur->prev) {
|
||||
cmark_node_set_item_index(cur, 1 + cmark_node_get_item_index(cur->prev));
|
||||
} else {
|
||||
cmark_node_set_item_index(cur, cmark_node_get_list_start(cur->parent));
|
||||
}
|
||||
}
|
||||
if (!render_node(&renderer, cur, ev_type, options)) {
|
||||
// a false value causes us to skip processing
|
||||
// the node's contents. this is used for
|
||||
// autolinks.
|
||||
cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
// ensure final newline
|
||||
if (renderer.buffer->size == 0 || renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') {
|
||||
cmark_strbuf_putc(renderer.buffer, '\n');
|
||||
}
|
||||
|
||||
result = (char *)cmark_strbuf_detach(renderer.buffer);
|
||||
|
||||
cmark_iter_free(iter);
|
||||
cmark_strbuf_free(renderer.prefix);
|
||||
cmark_strbuf_free(renderer.buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cmark-gfm.h"
|
||||
#include "syntax_extension.h"
|
||||
#include "buffer.h"
|
||||
|
||||
extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR;
|
||||
|
||||
static cmark_mem *_mem = &CMARK_DEFAULT_MEM_ALLOCATOR;
|
||||
|
||||
void cmark_syntax_extension_free(cmark_mem *mem, cmark_syntax_extension *extension) {
|
||||
if (extension->free_function && extension->priv) {
|
||||
extension->free_function(mem, extension->priv);
|
||||
}
|
||||
|
||||
cmark_llist_free(mem, extension->special_inline_chars);
|
||||
mem->free(extension->name);
|
||||
mem->free(extension);
|
||||
}
|
||||
|
||||
cmark_syntax_extension *cmark_syntax_extension_new(const char *name) {
|
||||
cmark_syntax_extension *res = (cmark_syntax_extension *) _mem->calloc(1, sizeof(cmark_syntax_extension));
|
||||
size_t size = strlen(name) + 1;
|
||||
res->name = (char *) _mem->calloc(size, sizeof(char));
|
||||
#if defined(_WIN32)
|
||||
strcpy_s(res->name, size, name);
|
||||
#else
|
||||
strcpy(res->name, name);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
cmark_node_type cmark_syntax_extension_add_node(int is_inline) {
|
||||
cmark_node_type *ref = !is_inline ? &CMARK_NODE_LAST_BLOCK : &CMARK_NODE_LAST_INLINE;
|
||||
|
||||
if ((*ref & CMARK_NODE_VALUE_MASK) == CMARK_NODE_VALUE_MASK) {
|
||||
assert(false);
|
||||
return (cmark_node_type) 0;
|
||||
}
|
||||
|
||||
return *ref = (cmark_node_type) ((int) *ref + 1);
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_emphasis(cmark_syntax_extension *extension,
|
||||
int emphasis) {
|
||||
extension->emphasis = emphasis == 1;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_open_block_func(cmark_syntax_extension *extension,
|
||||
cmark_open_block_func func) {
|
||||
extension->try_opening_block = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_match_block_func(cmark_syntax_extension *extension,
|
||||
cmark_match_block_func func) {
|
||||
extension->last_block_matches = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_match_inline_func(cmark_syntax_extension *extension,
|
||||
cmark_match_inline_func func) {
|
||||
extension->match_inline = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_inline_from_delim_func(cmark_syntax_extension *extension,
|
||||
cmark_inline_from_delim_func func) {
|
||||
extension->insert_inline_from_delim = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_special_inline_chars(cmark_syntax_extension *extension,
|
||||
cmark_llist *special_chars) {
|
||||
extension->special_inline_chars = special_chars;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_get_type_string_func(cmark_syntax_extension *extension,
|
||||
cmark_get_type_string_func func) {
|
||||
extension->get_type_string_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_can_contain_func(cmark_syntax_extension *extension,
|
||||
cmark_can_contain_func func) {
|
||||
extension->can_contain_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_contains_inlines_func(cmark_syntax_extension *extension,
|
||||
cmark_contains_inlines_func func) {
|
||||
extension->contains_inlines_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_commonmark_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func) {
|
||||
extension->commonmark_render_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_plaintext_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func) {
|
||||
extension->plaintext_render_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func) {
|
||||
extension->latex_render_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_xml_attr_func(cmark_syntax_extension *extension,
|
||||
cmark_xml_attr_func func) {
|
||||
extension->xml_attr_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension,
|
||||
cmark_common_render_func func) {
|
||||
extension->man_render_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_html_render_func(cmark_syntax_extension *extension,
|
||||
cmark_html_render_func func) {
|
||||
extension->html_render_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_html_filter_func(cmark_syntax_extension *extension,
|
||||
cmark_html_filter_func func) {
|
||||
extension->html_filter_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension,
|
||||
cmark_postprocess_func func) {
|
||||
extension->postprocess_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_private(cmark_syntax_extension *extension,
|
||||
void *priv,
|
||||
cmark_free_func free_func) {
|
||||
extension->priv = priv;
|
||||
extension->free_function = free_func;
|
||||
}
|
||||
|
||||
void *cmark_syntax_extension_get_private(cmark_syntax_extension *extension) {
|
||||
return extension->priv;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_opaque_alloc_func(cmark_syntax_extension *extension,
|
||||
cmark_opaque_alloc_func func) {
|
||||
extension->opaque_alloc_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_opaque_free_func(cmark_syntax_extension *extension,
|
||||
cmark_opaque_free_func func) {
|
||||
extension->opaque_free_func = func;
|
||||
}
|
||||
|
||||
void cmark_syntax_extension_set_commonmark_escape_func(cmark_syntax_extension *extension,
|
||||
cmark_commonmark_escape_func func) {
|
||||
extension->commonmark_escape_func = func;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user