diff --git a/MANUAL.txt b/MANUAL.txt index f337e0808..28030ae46 100644 --- a/MANUAL.txt +++ b/MANUAL.txt @@ -479,9 +479,26 @@ General options {.options} Reader options {.options} -------------- +`--shift-heading-level-by=`*NUMBER* + + Shift heading levels by a positive or negative integer. + For example, with `--shift-heading-level-by=-1`, level 2 + headings become level 1 headings, and level 3 headings + become level 2 headings. Headings cannot have a level + less than 1, so a heading that would be shifted below level 1 + becomes a regular paragraph. Exception: with a shift of -1, + a level-1 heading at the beginning of the document + replaces the metadata title. Conversely, with a shift + of +1, a nonempty metadata title becomes a level-1 heading at + the beginning of the document. `--shift-heading-level-by=-1` + is a good choice when converting HTML or Markdown documents that + use an initial level-1 heading for the document title and + level-2+ headings for sections. + `--base-header-level=`*NUMBER* -: Specify the base level for headings (defaults to 1). +: *Deprecated. Use `--shift-heading-level-by` instead.* + Specify the base level for headings (defaults to 1). `--strip-empty-paragraphs` diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs index 49b20f9dc..12f5de537 100644 --- a/src/Text/Pandoc/App.hs +++ b/src/Text/Pandoc/App.hs @@ -241,6 +241,9 @@ convertWithOpts opts = do let transforms = (case optBaseHeaderLevel opts of x | x > 1 -> (headerShift (x - 1) :) | otherwise -> id) . + (case optShiftHeadingLevel opts of + 0 -> id + x -> (headerShift x :)) . (if optStripEmptyParagraphs opts then (stripEmptyParagraphs :) else id) . diff --git a/src/Text/Pandoc/App/CommandLineOptions.hs b/src/Text/Pandoc/App/CommandLineOptions.hs index ae12ba42c..cffe69eca 100644 --- a/src/Text/Pandoc/App/CommandLineOptions.hs +++ b/src/Text/Pandoc/App/CommandLineOptions.hs @@ -436,9 +436,22 @@ options = "SCRIPTPATH") "" -- "Lua filter" - , Option "" ["base-header-level"] + , Option "" ["shift-heading-level-by"] (ReqArg (\arg opt -> + case safeRead arg of + Just t -> + return opt{ optShiftHeadingLevel = t } + _ -> E.throwIO $ PandocOptionError + "shift-heading-level-by takes an integer argument") + "NUMBER") + "" -- "Shift heading level" + + , Option "" ["base-header-level"] + (ReqArg + (\arg opt -> do + deprecatedOption "--base-header-level" + "Use --shift-heading-level-by instead." case safeRead arg of Just t | t > 0 && t < 6 -> return opt{ optBaseHeaderLevel = t } @@ -450,7 +463,7 @@ options = , Option "" ["strip-empty-paragraphs"] (NoArg (\opt -> do - deprecatedOption "--stripEmptyParagraphs" + deprecatedOption "--strip-empty-paragraphs" "Use +empty_paragraphs extension." return opt{ optStripEmptyParagraphs = True })) "" -- "Strip empty paragraphs" diff --git a/src/Text/Pandoc/App/Opt.hs b/src/Text/Pandoc/App/Opt.hs index 0b7bb7f2c..1111a5457 100644 --- a/src/Text/Pandoc/App/Opt.hs +++ b/src/Text/Pandoc/App/Opt.hs @@ -50,6 +50,7 @@ data Opt = Opt , optReader :: Maybe String -- ^ Reader format , optWriter :: Maybe String -- ^ Writer format , optTableOfContents :: Bool -- ^ Include table of contents + , optShiftHeadingLevel :: Int -- ^ Shift heading level by , optBaseHeaderLevel :: Int -- ^ Base header level , optTemplate :: Maybe FilePath -- ^ Custom template , optVariables :: [(String,String)] -- ^ Template variables to set @@ -124,6 +125,7 @@ defaultOpts = Opt , optReader = Nothing , optWriter = Nothing , optTableOfContents = False + , optShiftHeadingLevel = 0 , optBaseHeaderLevel = 1 , optTemplate = Nothing , optVariables = [] diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs index 06715145e..e169ccb82 100644 --- a/src/Text/Pandoc/Shared.hs +++ b/src/Text/Pandoc/Shared.hs @@ -565,10 +565,23 @@ isHeaderBlock _ = False -- | Shift header levels up or down. headerShift :: Int -> Pandoc -> Pandoc -headerShift n = walk shift - where shift :: Block -> Block - shift (Header level attr inner) = Header (level + n) attr inner - shift x = x +headerShift n (Pandoc meta (Header m _ ils : bs)) + | n < 0 + , m + n == 0 = headerShift n $ + B.setTitle (B.fromList ils) $ Pandoc meta bs +headerShift n (Pandoc meta bs) + | n > 0 + , not (null (docTitle meta)) + = Pandoc meta' (Header n nullAttr (docTitle meta) : bs') + where + Pandoc meta' bs' = headerShift n $ B.deleteMeta "title" $ Pandoc meta bs +headerShift n (Pandoc meta bs) = Pandoc meta (walk shift bs) + where + shift :: Block -> Block + shift (Header level attr inner) + | level + n > 0 = Header (level + n) attr inner + | otherwise = Para inner + shift x = x -- | Remove empty paragraphs. stripEmptyParagraphs :: Pandoc -> Pandoc diff --git a/test/command/shift-heading-level-by.md b/test/command/shift-heading-level-by.md new file mode 100644 index 000000000..1d8b8bdd0 --- /dev/null +++ b/test/command/shift-heading-level-by.md @@ -0,0 +1,33 @@ +``` +% pandoc --shift-heading-level-by 1 -t native -s +--- +title: My title +... + +# First heading + +## Second +^D +Pandoc (Meta {unMeta = fromList []}) +[Header 1 ("",[],[]) [Str "My",Space,Str "title"] +,Header 2 ("first-heading",[],[]) [Str "First",Space,Str "heading"] +,Header 3 ("second",[],[]) [Str "Second"]] +``` + +``` +% pandoc --shift-heading-level-by -1 -t native -s +--- +title: Old title +... + +# First heading + +## Second + +# Another top-level heading +^D +Pandoc (Meta {unMeta = fromList [("title",MetaInlines [Str "First",Space,Str "heading"])]}) +[Header 1 ("second",[],[]) [Str "Second"] +,Para [Str "Another",Space,Str "top-level",Space,Str "heading"]] +``` +