From 50672f5e5ee52f91eddb8a310ba952543db821af Mon Sep 17 00:00:00 2001 From: Devin Lehmacher Date: Sat, 5 Mar 2022 13:56:18 -0800 Subject: [PATCH 1/2] SourceRange.prettyRange: escape sourceName --- commonmark/src/Commonmark/Types.hs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/commonmark/src/Commonmark/Types.hs b/commonmark/src/Commonmark/Types.hs index a4d81ea..1516fe8 100644 --- a/commonmark/src/Commonmark/Types.hs +++ b/commonmark/src/Commonmark/Types.hs @@ -126,12 +126,12 @@ prettyRange (SourceRange xs) = go "" xs go _ [] = "" go curname ((p1,p2):rest) = (if sourceName p1 /= curname - then sourceName p1 ++ "@" + then escapeSourceName (sourceName p1) ++ "@" else "") ++ show (sourceLine p1) ++ ":" ++ show (sourceColumn p1) ++ "-" ++ (if sourceName p2 /= sourceName p1 - then sourceName p2 ++ "@" + then escapeSourceName (sourceName p2) ++ "@" else "") ++ show (sourceLine p2) ++ ":" ++ show (sourceColumn p2) ++ @@ -139,6 +139,17 @@ prettyRange (SourceRange xs) = go "" xs then "" else ";" ++ go (sourceName p2) rest +-- if the source name contains special characters it can lead to ambiguity when +-- a filename exactly matches a fragment of syntax of the range +escapeSourceName :: String -> String +escapeSourceName = concatMap escapeChar + where + escapeChar '-' = "%2D" + escapeChar '%' = "%25" + escapeChar ':' = "%3A" + escapeChar ';' = "%3B" + escapeChar x = [x] + type Attribute = (Text, Text) type Attributes = [Attribute] From 09b9ebb0a85c417be2afe779578adca6b77f035c Mon Sep 17 00:00:00 2001 From: Devin Lehmacher Date: Sat, 12 Mar 2022 14:08:36 -0800 Subject: [PATCH 2/2] add benchmarks for SourceName escaping --- commonmark/benchmark/benchmark.hs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/commonmark/benchmark/benchmark.hs b/commonmark/benchmark/benchmark.hs index 44c9441..625ad99 100644 --- a/commonmark/benchmark/benchmark.hs +++ b/commonmark/benchmark/benchmark.hs @@ -1,6 +1,8 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE FlexibleContexts #-} import Test.Tasty.Bench import Data.Text (Text) import Data.Functor.Identity -- base >= 4.8 @@ -19,6 +21,8 @@ main = do ] , bgroup "pathological" (map toPathBench pathtests) + , bgroup "name impact" + (map (toNameImpactBench sample) nameImpactTests) ] toPathBench :: (String, Int -> T.Text) -> Benchmark @@ -88,11 +92,31 @@ pathtests = ("a" <> T.replicate num " (String, String) -> Benchmark +toNameImpactBench sample (testName, name) = + let benchArgs n = (show n, take (50 * n) (cycle name), sample) + in bgroup testName + (map (benchCommonmark' @SourceRange defaultSyntaxSpec . benchArgs) + [1, 5, 10, 20]) + +nameImpactTests :: [(String, String)] +nameImpactTests = + [ ("no special characters", "the quick brown fox jumps over the lazy dog") + , ("special characters", "\\-:-as;df-:d:%%-:\\;;;\\-:%%-:---:-sdf-:sa-\\;") + ] + benchCommonmark :: SyntaxSpec Identity (Html ()) (Html ()) -> (String, Text) -> Benchmark benchCommonmark spec (name, contents) = - bench name $ + benchCommonmark' spec (name, name, contents) + +benchCommonmark' :: Rangeable (Html a) + => SyntaxSpec Identity (Html a) (Html a) + -> (String, String, Text) + -> Benchmark +benchCommonmark' spec (testName, name, contents) = + bench testName $ nf (either (error . show) renderHtml . runIdentity . parseCommonmarkWith spec . tokenize name) contents