Skip to content

Commit 5307940

Browse files
committed
Allow ':' in header-args key values
Also refactor code for future property drawer tangle args parsing support. Fixes #12.
1 parent 5d6678d commit 5307940

File tree

2 files changed

+86
-37
lines changed

2 files changed

+86
-37
lines changed

src/ntangle.nim

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ const
3434
dbg "{tangledExt}"
3535

3636
type
37+
HeaderArgType = enum
38+
haPropertyKwd
39+
haPropertyDrawer
40+
haBeginSrc
41+
haNone
3742
UserError = object of Exception
3843
OrgError = object of Exception
3944
HeaderArgs = object
@@ -42,7 +47,8 @@ type
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

362407
proc 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

408453
proc 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

477523
when isMainModule:

tests/wyag/libwyag.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ def show_ref(repo, refs, with_hash=True, prefix=""):
594594
else:
595595
show_ref(repo, v, with_hash=with_hash, prefix="{0}{1}{2}".format(prefix, "/" if prefix else "", k))
596596

597+
class GitTag(GitCommit):
598+
fmt = b'tag'
599+
597600
argsp = argsubparsers.add_parser(
598601
"tag",
599602
help="List and create tags")

0 commit comments

Comments
 (0)