├── .github └── workflows │ ├── lint.yml │ └── test.yml ├── LICENSE ├── README.org ├── flake.lock ├── flake.nix ├── nix ├── default.nix ├── dropUntil.nix ├── dropWhile.nix ├── excludeOrgSubtreesOnHeadlines.nix ├── matchOrgHeadline.nix ├── matchOrgTag.nix ├── parseParamsString.nix ├── selectHeadlines.nix ├── splitWith.nix ├── tangleOrgBabel.nix └── utils.nix └── test ├── flake.lock ├── flake.nix ├── test.nix ├── test.org ├── testParams.nix └── testTangle.org /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | pull_request: 4 | paths-ignore: 5 | - 'README.*' 6 | push: 7 | branches: 8 | - master 9 | paths-ignore: 10 | - 'README.*' 11 | workflow_dispatch: 12 | jobs: 13 | lint: 14 | uses: emacs-twist/internal/.github/workflows/check.yml@master 15 | with: 16 | package: linters 17 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: 4 | paths: 5 | - '**/*.nix' 6 | push: 7 | branches: 8 | - master 9 | paths: 10 | - '**/*.nix' 11 | workflow_dispatch: 12 | jobs: 13 | check: 14 | strategy: 15 | matrix: 16 | os: 17 | - ubuntu-latest 18 | - macos-latest 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: cachix/install-nix-action@V28 23 | with: 24 | nix_path: nixpkgs=channel:nixos-unstable 25 | extra_nix_config: | 26 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 27 | - name: Run tests 28 | run: nix-instantiate --eval --strict test/test.nix 29 | - name: Test parameter parsing 30 | run: nix-instantiate --eval --strict test/testParams.nix 31 | - name: Check the derivation 32 | run: nix flake check --no-write-lock-file --override-input org-babel "path:$PWD/.." 33 | working-directory: test 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021,2022 Emacs Twist 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * nix-org-babel 2 | This is an implementation of =org-babel-tangle= in Nix. 3 | Given an Org string, it extracts the contents of source blocks in a particular language. 4 | ** Rationale 5 | [[https://github.com/talyz/fromElisp][talyz/fromElisp]] provides support for Org, and it is also part of [[https://github.com/nix-community/emacs-overlay][emacs-overlay]], which apparently is used by many people for their own configs. 6 | However, I wanted to experiment with more advanced features such as excluding archived entries, and I wasn't sure if I could mix that with their code base. 7 | I needed the feature in [[https://github.com/akirak/emacs-twist][my own project]], and I wanted to put it under my control, so I wrote it from scratch. 8 | ** Usage 9 | Import the flake. 10 | *** Extracting source blocks from an Org file/string 11 | **** tangleOrgBabel function 12 | This function takes an Org string and returns its source blocks. 13 | 14 | Example: 15 | 16 | #+begin_src nix 17 | let 18 | tangle = lib.tangleOrgBabel { 19 | languages = [ "emacs-lisp" ]; 20 | }; 21 | in 22 | # Return a string 23 | tangle (builtins.readFile ./config.org) 24 | #+end_src 25 | 26 | Arguments: 27 | 28 | 1. An attribute set of options. 29 | 2. An Org input string. 30 | **** tangleOrgBabelFile function 31 | Similar to =tangleOrgBabel=, but this function takes a file as an argument and writes the output to a file. 32 | 33 | Example: 34 | 35 | #+begin_src nix 36 | # Write to a file 37 | let 38 | pkgs = import nixpkgs { 39 | inherit system; 40 | overlays = [ 41 | org-babel.overlays.default 42 | ]; 43 | }; 44 | in 45 | pkgs.tangleOrgBabelFile "init.el" ./config.org { 46 | languages = [ "emacs-lisp" ]; 47 | } 48 | #+end_src 49 | 50 | Note that this function is provided in the overlay of the flake. 51 | 52 | Arguments: 53 | 54 | 1. A string for the derivation name. 55 | 2. An input file path. 56 | 3. An attribute set of options. 57 | *** Options 58 | **** Languages 59 | Example: 60 | 61 | #+begin_src nix 62 | { 63 | languages = [ "emacs-lisp" "elisp" ]; 64 | } 65 | #+end_src 66 | 67 | Default: =[ "emacs-lisp" "elisp" ]= 68 | **** Filtering subtrees 69 | You can transform the input by specifying =processLines= option. 70 | It must be a function that takes a list of strings and returns a list of strings. 71 | 72 | This library also contains =excludeHeadlines= function which can be used to exclude subtrees according to a predicate on the headline text, so you can use it in the option. 73 | 74 | Example: 75 | 76 | #+begin_src nix 77 | { 78 | # Exclude archived subtrees 79 | processLines = excludeHeadlines (matchOrgTag "ARCHIVE"); 80 | } 81 | #+end_src 82 | 83 | You can use the following predicates from this library: 84 | 85 | - tag :: Returns true if the headline matches a tag given as the argument. The argument must be a string. 86 | - headlineText :: Returns true if the headline matches the text given as the argument. The argument must be a string. 87 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "root": {} 4 | }, 5 | "root": "root", 6 | "version": 7 7 | } 8 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Nix library for extracting source blocks from Org"; 3 | 4 | outputs = _: 5 | let 6 | lib = import ./nix; 7 | in 8 | { 9 | inherit lib; 10 | overlays = { 11 | default = _: prev: { 12 | tangleOrgBabelFile = name: path: options: 13 | prev.writeText name 14 | (lib.tangleOrgBabel options (builtins.readFile path)); 15 | }; 16 | }; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | with builtins; 2 | let 3 | excludeOrgSubtreesOnHeadlines = import ./excludeOrgSubtreesOnHeadlines.nix; 4 | selectHeadlines = import ./selectHeadlines.nix; 5 | 6 | matchOrgTag = import ./matchOrgTag.nix; 7 | matchOrgHeadline = import ./matchOrgHeadline.nix; 8 | matchOrgHeadlines = headlines: s: 9 | builtins.any (t: matchOrgHeadline t s) headlines; 10 | 11 | tangleOrgBabel = import ./tangleOrgBabel.nix; 12 | in 13 | { 14 | # Newer concise APIs 15 | excludeHeadlines = excludeOrgSubtreesOnHeadlines; 16 | inherit selectHeadlines; 17 | tag = matchOrgTag; 18 | headlineText = matchOrgHeadline; 19 | allP = predicates: x: builtins.all (p: p x) predicates; 20 | anyP = predicates: x: builtins.any (p: p x) predicates; 21 | 22 | # Deprecated APIs 23 | inherit matchOrgTag matchOrgHeadline matchOrgHeadlines; 24 | inherit excludeOrgSubtreesOnHeadlines; 25 | 26 | # Tangle 27 | inherit tangleOrgBabel; 28 | } 29 | -------------------------------------------------------------------------------- /nix/dropUntil.nix: -------------------------------------------------------------------------------- 1 | pred: items: 2 | with builtins; 3 | let 4 | xs = import ./dropWhile.nix (x: ! (pred x)) items; 5 | in 6 | if length xs > 0 7 | then tail xs 8 | else [ ] 9 | -------------------------------------------------------------------------------- /nix/dropWhile.nix: -------------------------------------------------------------------------------- 1 | pred: 2 | with builtins; 3 | let 4 | go = items: 5 | if length items == 0 6 | then [ ] 7 | else if ! (pred (head items)) 8 | then items 9 | else go (tail items); 10 | in 11 | go 12 | -------------------------------------------------------------------------------- /nix/excludeOrgSubtreesOnHeadlines.nix: -------------------------------------------------------------------------------- 1 | # Exclude lines of Org subtrees by a heading predicate 2 | pred: 3 | with builtins; 4 | let 5 | inherit (import ./utils.nix) 6 | dropTillSubtreeEnd 7 | getHeadlineLevel 8 | splitListWith 9 | isHeadline; 10 | 11 | go_ = cut: 12 | cut.before 13 | ++ 14 | (if cut.sep == null 15 | then [ ] 16 | else go (dropTillSubtreeEnd (getHeadlineLevel cut.sep) cut.after)); 17 | 18 | go = lines: go_ (splitListWith (s: isHeadline s && pred s) lines); 19 | 20 | in 21 | go 22 | -------------------------------------------------------------------------------- /nix/matchOrgHeadline.nix: -------------------------------------------------------------------------------- 1 | headlineText: s: 2 | builtins.match 3 | "\\*+[[:space:]]+${headlineText}([[:space:]]+:[^[:space:]]+:)?[[:space:]]*" 4 | s != null 5 | -------------------------------------------------------------------------------- /nix/matchOrgTag.nix: -------------------------------------------------------------------------------- 1 | tag: s: 2 | builtins.match 3 | "\\*+[[:space:]].+:${tag}:.*" 4 | s != null 5 | -------------------------------------------------------------------------------- /nix/parseParamsString.nix: -------------------------------------------------------------------------------- 1 | with builtins; 2 | let 3 | takeWhile = p: xs: 4 | if length xs == 0 5 | then [ ] 6 | else if p (head xs) 7 | then [ (head xs) ] ++ takeWhile p (tail xs) 8 | else [ ]; 9 | 10 | dropWhile = p: xs: 11 | if length xs == 0 12 | then [ ] 13 | else if p (head xs) 14 | then dropWhile p (tail xs) 15 | else xs; 16 | 17 | isKeyword = s: stringLength s > 0 && substring 0 1 s == ":"; 18 | 19 | notKeyword = s: !(isKeyword s); 20 | 21 | toValue = xs: 22 | if length xs == 0 23 | then true 24 | else if length xs == 1 25 | then head xs 26 | else xs; 27 | 28 | listToAttrs = xs: 29 | if length xs == 0 30 | then { } 31 | else { 32 | ${head xs} = toValue (takeWhile notKeyword (tail xs)); 33 | } // listToAttrs (dropWhile notKeyword (tail xs)); 34 | 35 | stripPat = "[[:space:]]+(.*)"; 36 | 37 | stripLeft = string: 38 | if match stripPat string != null 39 | then head (match stripPat string) 40 | else string; 41 | 42 | pat1 = "\"([^\"]+)\"(.*)"; 43 | 44 | pat2 = "([^\"][^[:space:]]*)(.*)"; 45 | 46 | go = m: [ (elemAt m 0) ] ++ parse (elemAt m 1); 47 | 48 | # TODO: Handle expressions 49 | # 50 | # This will be hard, as it requires an elisp parser. 51 | # It is probably better to unsupport it. 52 | parse' = string: 53 | if string == "" 54 | then [ ] 55 | else if match pat1 string != null 56 | then go (match pat1 string) 57 | else if match pat2 string != null 58 | then go (match pat2 string) 59 | else throw "Match nothing: ${string}"; 60 | 61 | parse = string: parse' (stripLeft string); 62 | in 63 | string: 64 | listToAttrs (parse string) 65 | -------------------------------------------------------------------------------- /nix/selectHeadlines.nix: -------------------------------------------------------------------------------- 1 | # Select headlines matching a tag/headline 2 | pred: 3 | with builtins; 4 | let 5 | inherit (import ./utils.nix) 6 | makeSubtreeEndRegexp 7 | getHeadlineLevel 8 | splitListWith 9 | dropWhile 10 | isHeadline; 11 | 12 | makeEndPred = headline: 13 | s: isHeadline s 14 | && match (makeSubtreeEndRegexp (getHeadlineLevel headline)) s != null; 15 | 16 | go0 = lines: 17 | if length lines == 0 18 | then [ ] 19 | else go1 (head lines) (splitListWith (makeEndPred (head lines)) (tail lines)); 20 | 21 | go1 = initial: cut: 22 | [ initial ] 23 | ++ 24 | cut.before 25 | ++ 26 | (if cut.sep == null 27 | then [ ] 28 | else go ([ cut.sep ] ++ cut.after)); 29 | 30 | go = lines: 31 | if length lines == 0 32 | then [ ] 33 | else go0 (dropWhile (s: !(isHeadline s && pred s)) lines); 34 | in 35 | go 36 | -------------------------------------------------------------------------------- /nix/splitWith.nix: -------------------------------------------------------------------------------- 1 | pred: 2 | with builtins; 3 | let 4 | go = before: rest: 5 | if (length rest == 0) 6 | then { inherit before; sep = null; after = [ ]; } 7 | else if pred (head rest) 8 | then { inherit before; sep = head rest; after = tail rest; } 9 | else (go (before ++ [ (head rest) ]) (tail rest)); 10 | in 11 | go [ ] 12 | -------------------------------------------------------------------------------- /nix/tangleOrgBabel.nix: -------------------------------------------------------------------------------- 1 | # Quick-and-dirty re-implementation of org-babel-tangle in Nix. 2 | { languages ? [ "emacs-lisp" "elisp" ] 3 | , processLines ? lines: lines 4 | , tangleArg ? "yes" 5 | }: 6 | string: 7 | with builtins; 8 | let 9 | lines = filter isString (split "\n" string); 10 | 11 | dropUntil = import ./dropUntil.nix; 12 | 13 | blockStartRegexp = 14 | "[[:space:]]*\#\\+[Bb][Ee][Gg][Ii][Nn]_[Ss][Rr][Cc][[:space:]]+" 15 | + "(" + (concatStringsSep "|" languages) + ")" 16 | + "(([[:space:]].*)?)"; 17 | 18 | parseParamsString = import ./parseParamsString.nix; 19 | 20 | parseParamsString' = s: 21 | if s == null 22 | then { } 23 | else parseParamsString s; 24 | 25 | checkBlockParams = attrs: 26 | foldl' (acc: value: acc && value) true 27 | (attrValues 28 | (mapAttrs (name: value: 29 | if name == ":tangle" 30 | then value == tangleArg 31 | else true 32 | ) attrs)); 33 | 34 | isBlockStart = line: 35 | (match blockStartRegexp line != null) 36 | && checkBlockParams (parseParamsString' (elemAt (match blockStartRegexp line) 2)); 37 | 38 | splitListWith = import ./splitWith.nix; 39 | 40 | blockEndRegexp = "[[:space:]]*\#\\+[Ee][Nn][Dd]_[Ss][Rr][Cc].*"; 41 | 42 | isBlockEnd = line: match blockEndRegexp line != null; 43 | 44 | go = acc: xs: 45 | let 46 | st1 = dropUntil isBlockStart xs; 47 | st2 = splitListWith isBlockEnd st1; 48 | in 49 | if length xs == 0 50 | then acc 51 | else if length st1 == 0 52 | then acc 53 | else (go (acc ++ [ st2.before ]) st2.after); 54 | 55 | in 56 | concatStringsSep "\n" (concatLists (go [ ] (processLines lines))) 57 | -------------------------------------------------------------------------------- /nix/utils.nix: -------------------------------------------------------------------------------- 1 | with builtins; 2 | let 3 | genericHeadlineRegexp = "(\\*+)[[:space:]].+"; 4 | 5 | prependOptionalStars = n: rest: 6 | if n == 0 7 | then rest 8 | else prependOptionalStars (n - 1) ("\\*?" + rest); 9 | in 10 | rec { 11 | dropWhile = import ./dropWhile.nix; 12 | 13 | splitListWith = import ./splitWith.nix; 14 | 15 | isHeadline = s: substring 0 1 s == "*"; 16 | 17 | getHeadlineLevel = headline: 18 | stringLength (head (match genericHeadlineRegexp headline)); 19 | 20 | makeSubtreeEndRegexp = outlineLevel: 21 | prependOptionalStars (outlineLevel - 1) "\\*[[:space:]].+"; 22 | 23 | dropTillSubtreeEnd = level: 24 | dropWhile (s: !(isHeadline s && match (makeSubtreeEndRegexp level) s != null)); 25 | } 26 | -------------------------------------------------------------------------------- /test/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1710146030, 9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "id": "flake-utils", 17 | "type": "indirect" 18 | } 19 | }, 20 | "nixpkgs": { 21 | "locked": { 22 | "lastModified": 1725534445, 23 | "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=", 24 | "owner": "NixOS", 25 | "repo": "nixpkgs", 26 | "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39", 27 | "type": "github" 28 | }, 29 | "original": { 30 | "id": "nixpkgs", 31 | "type": "indirect" 32 | } 33 | }, 34 | "org-babel": { 35 | "locked": { 36 | "lastModified": 1724939881, 37 | "narHash": "sha256-p6GB2AyyFb77s6gZct7rnVqoZ1Ahu+0rcTtI/oxh25I=", 38 | "owner": "emacs-twist", 39 | "repo": "org-babel", 40 | "rev": "ec71fec7ec0f363a3868c7ecc7bdf1cfdaecd9c8", 41 | "type": "github" 42 | }, 43 | "original": { 44 | "owner": "emacs-twist", 45 | "repo": "org-babel", 46 | "type": "github" 47 | } 48 | }, 49 | "root": { 50 | "inputs": { 51 | "flake-utils": "flake-utils", 52 | "nixpkgs": "nixpkgs", 53 | "org-babel": "org-babel" 54 | } 55 | }, 56 | "systems": { 57 | "locked": { 58 | "lastModified": 1681028828, 59 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 60 | "owner": "nix-systems", 61 | "repo": "default", 62 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 63 | "type": "github" 64 | }, 65 | "original": { 66 | "owner": "nix-systems", 67 | "repo": "default", 68 | "type": "github" 69 | } 70 | } 71 | }, 72 | "root": "root", 73 | "version": 7 74 | } 75 | -------------------------------------------------------------------------------- /test/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs.org-babel.url = "github:emacs-twist/org-babel"; 3 | 4 | outputs = { 5 | org-babel, 6 | flake-utils, 7 | nixpkgs, 8 | ... 9 | }: 10 | flake-utils.lib.eachSystem [ 11 | "x86_64-linux" 12 | "x86_64-darwin" 13 | "aarch64-linux" 14 | "aarch64-darwin" 15 | ] (system: let 16 | pkgs = import nixpkgs { 17 | inherit system; 18 | overlays = [ 19 | org-babel.overlays.default 20 | ]; 21 | }; 22 | in { 23 | checks = { 24 | build = 25 | pkgs.tangleOrgBabelFile "example" ./testTangle.org 26 | {}; 27 | }; 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /test/test.nix: -------------------------------------------------------------------------------- 1 | # nix-instantiate --eval --strict test/test.nix 2 | with builtins; 3 | let 4 | pkgs = import { }; 5 | exclude = import ../nix/excludeOrgSubtreesOnHeadlines.nix; 6 | select = import ../nix/selectHeadlines.nix; 7 | matchOrgTag = import ../nix/matchOrgTag.nix; 8 | matchOrgHeadline = import ../nix/matchOrgHeadline.nix; 9 | dropUntil = import ../nix/dropUntil.nix; 10 | tangleOrgBabel = import ../nix/tangleOrgBabel.nix; 11 | lines = filter (s: isString s && s != "") (split "\n" (readFile ./test.org)); 12 | in 13 | pkgs.lib.runTests { 14 | testTagPredicate = { 15 | expr = head (exclude (matchOrgTag "ARCHIVE") lines); 16 | expected = "* Good morning"; 17 | }; 18 | 19 | testHeadlinePredicate = { 20 | expr = head (exclude (matchOrgHeadline "Archived entry") lines); 21 | expected = "* Good morning"; 22 | }; 23 | 24 | testSubtree = { 25 | expr = head (dropUntil (s: s == "Hello!") (exclude (matchOrgTag "irregular") lines)); 26 | expected = "* Get to work"; 27 | }; 28 | 29 | testTagPredicate2 = { 30 | expr = pkgs.lib.last (exclude (matchOrgTag "optional") lines); 31 | expected = "It was a good day!"; 32 | }; 33 | 34 | testSelect1 = { 35 | expr = pkgs.lib.last (select (matchOrgTag "irregular") lines); 36 | expected = "Why did he come to the office in the first place?"; 37 | }; 38 | 39 | testSelect2 = { 40 | expr = pkgs.lib.last (select (matchOrgTag "optional") lines); 41 | expected = "/Zaijian/ means goodbye in Mandarin Chinese."; 42 | }; 43 | 44 | testTangleOrgBabel = { 45 | expr = pkgs.lib.pipe (tangleOrgBabel { } (readFile ./testTangle.org)) [ 46 | (split "\n") 47 | (filter isString) 48 | ]; 49 | 50 | expected = [ 51 | "Default" 52 | "Alternative language name" 53 | "Upper case" 54 | ":tangle yes" 55 | "Extra spaces around params" 56 | ]; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /test/test.org: -------------------------------------------------------------------------------- 1 | * Archived entry :ARCHIVE: 2 | ** Child inside an archive entry 3 | * Good morning 4 | Good morning! 5 | * Zao :optional: 6 | /Zao/ means good morning in Mandarin Chinese. 7 | Chinese is optional for non-Chinese people living in countries other than China. 8 | * Hello 9 | Hello! 10 | ** Irreguar cases :irregular: 11 | *** If he/she isn't fine 12 | Call an ambulance. 13 | Why did he come to the office in the first place? 14 | * Get to work 15 | Do you love your work? 16 | * Bye 17 | Bye! 18 | It was a good day! 19 | * Zaijian :optional: 20 | /Zaijian/ means goodbye in Mandarin Chinese. 21 | -------------------------------------------------------------------------------- /test/testParams.nix: -------------------------------------------------------------------------------- 1 | # nix-instantiate --eval --strict test/testParams.nix 2 | let 3 | pkgs = import {}; 4 | parse = import ../nix/parseParamsString.nix; 5 | in 6 | pkgs.lib.runTests { 7 | testSimple = { 8 | expr = parse ":tangle no"; 9 | expected = { 10 | ":tangle" = "no"; 11 | }; 12 | }; 13 | 14 | testOdd = { 15 | expr = parse ":async"; 16 | expected = { 17 | ":async" = true; 18 | }; 19 | }; 20 | 21 | testNonAlpha = { 22 | expr = parse ":session kernel-16577-ssh.json"; 23 | expected = { 24 | ":session" = "kernel-16577-ssh.json"; 25 | }; 26 | }; 27 | 28 | # This case is unsupported right now. 29 | # 30 | # testExpression = { 31 | # expr = parse ":value '(this is so annoying)"; 32 | # expected = { }; 33 | # }; 34 | 35 | testDoubleQuotes = { 36 | expr = parse ":caption \"Hello, I am Katja\""; 37 | expected = { 38 | ":caption" = "Hello, I am Katja"; 39 | }; 40 | }; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/testTangle.org: -------------------------------------------------------------------------------- 1 | #+begin_src 2 | No language 3 | #+end_src 4 | 5 | #+begin_src emacs-lisp 6 | Default 7 | #+end_src 8 | 9 | #+begin_src elisp 10 | Alternative language name 11 | #+end_src elisp 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | Upper case 15 | #+END_SRC 16 | 17 | #+BEGIN_SRC shell 18 | Different language 19 | #+END_SRC 20 | 21 | #+begin_src emacs-lisp :tangle yes 22 | :tangle yes 23 | #+end_src 24 | 25 | #+begin_src emacs-lisp :tangle yes 26 | Extra spaces around params 27 | #+end_src 28 | 29 | #+begin_src emacs-lisp :tangle no 30 | :tangle no 31 | #+end_src 32 | --------------------------------------------------------------------------------