3434dbg " {tangledExt}"
3535
3636type
37+ HeaderArgType = enum
38+ haPropertyKwd
39+ haPropertyDrawer
40+ haBeginSrc
41+ haNone
3742 UserError = object of Exception
3843 OrgError = object of Exception
3944 HeaderArgs = object
4247 shebang: string
4348 mkdirp: bool
4449 permissions: set [FilePermission ]
45- GlobalHeaderArgs = tuple
50+ LangAndArgs = tuple
51+ argType: HeaderArgType
4652 lang: string
4753 args: seq [string ]
4854 LevelLangIndex = tuple
@@ -143,11 +149,8 @@ proc parseTangleHeaderProperties(hdrArgs: seq[string], lnum: int, lang: string,
143149 for hdrArg in hdrArgs:
144150 let
145151 hdrArgParts = hdrArg.strip.split (" " , maxsplit= 1 )
146- if hdrArgParts.len != 2 :
147- raise newException (OrgError , fmt (" Line {lnum} - The header arg ':{hdrArgParts[0]}' is missing its value." ))
148- let
149152 arg = hdrArgParts[0 ]
150- argval = hdrArgParts[1 ]. strip (chars = { '"' }) # treat :tangle foo.ext and :tangle "foo.ext" the same
153+ argval = hdrArgParts[1 ]
151154 dbg " arg={arg}, argval={argval}, onBeginSrc={onBeginSrc}, outfile={outfile}"
152155 case arg
153156 of " tangle" :
@@ -250,7 +253,7 @@ proc parseTangleHeaderProperties(hdrArgs: seq[string], lnum: int, lang: string,
250253 dbg " ** outFileName now set to {outFileName}"
251254 fileHeaderArgs[outFileName] = hArgs
252255
253- dbg " line={lnum}, onBeginSrc={onBeginSrc}, outfile={outfile} | outFileName={outFileName}"
256+ dbg " line={lnum}, onBeginSrc={onBeginSrc}, hArgs.tangle={hArgs.tangle} outfile={outfile} | outFileName={outFileName}"
254257 if onBeginSrc and (hArgs.tangle != " no" ):
255258 doAssert outFileName != " "
256259 dbg " line {lnum}: buffering enabled for `{outFileName}'"
@@ -337,27 +340,69 @@ proc updateHeaderArgsDefault() =
337340 discard
338341 prevOrgLevel = orgLevel
339342
340- proc parsePropertyHeaderArgs (line : string ): GlobalHeaderArgs =
341- # # Parse ``#+property: header-args`` type property Org keywords .
343+ proc getHeaderArgs (s : string ): LangAndArgs =
344+ # # Get well-formatted header args .
342345 # #
343346 # # Examples:
344- # # #+property: header-args:nim :tangle yes
345- # # #+property: header-args :tangle no
347+ # #
348+ # # " #+BEGIN_SRC nim :tangle \"hello.nim\" :flags -d:release "
349+ # # "#+property: header-args:nim :tangle hello.nim :flags -d:release"
350+ # # "#+property: HEADER-ARGS :tangle hello.nim :flags -d:release"
351+ # # " :header-args: :tangle hello.nim :flags -d:release"
352+ # #
353+ # # All of the above inputs will result in the below string sequence
354+ # # for the ``args`` field of ``LandAndArgs``:
355+ # # -> @["tangle hello.nim", "flags -d:release"]
356+ # # The ``lang`` field will be an empty string or a language string
357+ # # like ``"nim"``.
346358 let
347- lineParts = line.strip.split (" " )
348- linePartsLower = lineParts.mapIt (it.toLowerAscii.strip ())
349- if (lineParts.len >= 3 and
350- linePartsLower[0 ] == " #+property:" and
351- linePartsLower[1 ].startsWith (" header-args" )):
359+ spaceSepParts = s.strip.split (" " )
360+ var
361+ haType: HeaderArgType = haNone
362+ headerArgsRaw: seq [string ] = @ []
363+ headerArgs: seq [string ] = @ []
364+ headerArgPair: string
365+ lang: string
366+ dbg " spaceSepParts: {spaceSepParts}"
367+ if spaceSepParts.len >= 3 and
368+ spaceSepParts[0 ].toLowerAscii () == " #+property:" :
369+ headerArgsRaw = spaceSepParts[2 .. spaceSepParts.high]
352370 let
353- headerArgsKwdParts = lineParts[1 ].strip.split (" :" )
354- lang = if headerArgsKwdParts.len == 2 : # Example: "header-args:nim" -> @["header-args", "nim"]
355- headerArgsKwdParts[1 ].strip ()
356- else :
357- " "
358- hdrArgs = lineParts[2 .. lineParts.high].join (" " ).split (" :" ).mapIt (it.strip ())
359- doAssert hdrArgs.len >= 2
360- return (lang, hdrArgs[1 .. hdrArgs.high]) # The first element will always be "".
371+ kwdParts = spaceSepParts[1 ].split (" :" )
372+ if kwdParts.len == 2 :
373+ lang = kwdParts[1 ].strip ()
374+ haType = haPropertyKwd
375+ # ":header-args:", ":header-args+:", ":header-args:nim:"
376+ elif spaceSepParts.len >= 3 and
377+ spaceSepParts[0 ].toLowerAscii ().startsWith (" :header-args" ):
378+ headerArgsRaw = spaceSepParts[1 .. spaceSepParts.high]
379+ let
380+ kwdParts = spaceSepParts[0 ].split (" :" )
381+ if kwdParts.len == 4 :
382+ lang = kwdParts[2 ].strip (chars = {' ' , '+' })
383+ haType = haPropertyDrawer
384+ elif spaceSepParts.len >= 2 and
385+ spaceSepParts[0 ].toLowerAscii () == " #+begin_src" :
386+ if spaceSepParts.len >= 3 :
387+ headerArgsRaw = spaceSepParts[2 .. spaceSepParts.high]
388+ lang = spaceSepParts[1 ].strip ()
389+ haType = haBeginSrc
390+ if haType != haNone:
391+ # echo headerArgsRaw
392+ for i, h in headerArgsRaw:
393+ if h.len >= 2 and h[0 ] == ':' :
394+ if i == headerArgsRaw.high:
395+ raise newException (OrgError , fmt (" The header args are ending with a ':key' which is not valid. Found {headerArgsRaw}" ))
396+ if headerArgPair != " " :
397+ headerArgs.add (headerArgPair)
398+ headerArgPair = h[1 .. h.high]
399+ # echo fmt"{i} - {headerArgPair}"
400+ else :
401+ headerArgPair = headerArgPair & " " & h.strip (chars = {'"' })
402+ # echo fmt"{i} - {headerArgPair}"
403+ if i == headerArgsRaw.high:
404+ headerArgs.add (headerArgPair)
405+ return (haType, lang, headerArgs)
361406
362407proc lineAction (line: string , lnum: int ) =
363408 # # On detection of "#+begin_src" with ":tangle foo", enable
@@ -368,15 +413,19 @@ proc lineAction(line: string, lnum: int) =
368413 dbg " orgLevel = {orgLevel}"
369414 updateHeaderArgsDefault ()
370415 let
371- lineParts = line.strip. split ( " : " )
372- linePartsLower = lineParts. mapIt (it.toLowerAscii. strip ())
373- (propHeaderArgLang, propHeaderArgArgs) = line. parsePropertyHeaderArgs ()
374- if propHeaderArgArgs != @ [] :
375- dbg " Property header-args found [Lang={propHeaderArgLang }]: {propHeaderArgArgs }"
376- parseTangleHeaderProperties (propHeaderArgArgs , lnum, propHeaderArgLang , false )
416+ (haType, haLang, haArgs) = line.getHeaderArgs ( )
417+ dbg " [line {lnum}] {line} "
418+ dbg " getHeaderArgs: line {lnum}:: {haType}, {haLang}, {haArgs} "
419+ if haType in {haPropertyKwd} :
420+ dbg " Property header-args found [Lang={haLang }]: {haArgs }"
421+ parseTangleHeaderProperties (haArgs , lnum, haLang , false )
377422 else :
423+ let
424+ lineParts = line.strip.split (" :" )
425+ linePartsLower = lineParts.mapIt (it.toLowerAscii.strip ())
378426 if firstLineSrcBlock:
379427 dbg " first line of src block"
428+ dbg " line {lnum}: bufEnabled: {bufEnabled} linePartsLower: {linePartsLower}"
380429 if bufEnabled:
381430 if (linePartsLower[0 ] == " #+end_src" ):
382431 bufEnabled = false
@@ -390,20 +439,16 @@ proc lineAction(line: string, lnum: int) =
390439 blockIndent = (line.len - line.strip (trailing= false ).len)
391440
392441 try :
442+ doAssert outFileName != " "
393443 if firstLineSrcBlock and fileHeaderArgs[outFileName].padline:
394444 fileData[outFileName].add (" \n " )
395445 fileData[outFileName].add (lineAdjust (line, blockIndent))
396446 except KeyError : # If outFileName key is not yet set in fileData
397447 fileData[outFileName] = lineAdjust (line, blockIndent)
398448 dbg " extra indentation: {blockIndent}"
399449 firstLineSrcBlock = false
400- else :
401- let
402- firstPartParts = linePartsLower[0 ].split (" " )
403- if (firstPartParts[0 ] == " #+begin_src" ) and (firstPartParts.len >= 2 ): # Line needs to begin with "#+begin_src LANG"
404- let
405- lang = firstPartParts[1 ]
406- parseTangleHeaderProperties (lineParts[1 .. lineParts.high], lnum, lang, true )
450+ elif haType == haBeginSrc:
451+ parseTangleHeaderProperties (haArgs, lnum, haLang, true )
407452
408453proc writeFiles () =
409454 # # Write the files from ``fileData``.
@@ -471,7 +516,8 @@ proc ntangle(orgFilesOrDirs: seq[string]) =
471516 else :
472517 raise newException (UserError , fmt (" {f1} is neither a valid file nor a directory" ))
473518 except :
474- stderr.styledWriteLine (fgRed, " [ERROR] " , fgDefault, getCurrentExceptionMsg () & " \n " )
519+ stderr.styledWriteLine (fgRed, fmt" [ERROR] { getCurrentException ().name} : " ,
520+ fgDefault, getCurrentExceptionMsg () & " \n " )
475521 quit 1
476522
477523when isMainModule :
0 commit comments