xref: /Universal-ctags/parsers/rmarkdown.c (revision 3bbeaff45d748874f566350ab065b5510e382c16)
1 /*
2  *
3  *  Copyright (c) 2022, Masatake YAMATO
4  *
5  *   This source code is released for free distribution under the terms of the
6  *   GNU General Public License version 2 or (at your option) any later version.
7  *
8  * This module contains functions for generating tags for R Markdown files.
9  * https://bookdown.org/yihui/rmarkdown/
10  *
11  */
12 
13 /*
14  *   INCLUDE FILES
15  */
16 #include "general.h"	/* must always come first */
17 #include "markdown.h"
18 
19 #include "entry.h"
20 #include "parse.h"
21 
22 #include <ctype.h>
23 #include <string.h>
24 
25 /*
26  *   DATA DEFINITIONS
27  */
28 typedef enum {
29 	K_CHUNK_LABEL = 0,
30 } rmarkdownKind;
31 
32 static kindDefinition RMarkdownKinds[] = {
33 	{ true, 'l', "chunklabel",       "chunk labels"},
34 };
35 
36 struct sRMarkdownSubparser {
37 	markdownSubparser markdown;
38 };
39 
40 /*
41 *   FUNCTION DEFINITIONS
42 */
43 
findRMarkdownTags(void)44 static void findRMarkdownTags (void)
45 {
46 	scheduleRunningBaseparser (0);
47 }
48 
49 #define skip_space(CP) 	while (*CP == ' ' || *CP == '\t') CP++;
50 
makeRMarkdownTag(vString * name,int kindIndex,bool anonymous)51 static void makeRMarkdownTag (vString *name, int kindIndex, bool anonymous)
52 {
53 	tagEntryInfo e;
54 	initTagEntry (&e, vStringValue (name), kindIndex);
55 	if (anonymous)
56 		markTagExtraBit (&e, XTAG_ANONYMOUS);
57 	makeTagEntry (&e);
58 }
59 
extractLanguageForCodeBlock(markdownSubparser * s,const char * langMarker,vString * langName)60 static bool extractLanguageForCodeBlock (markdownSubparser *s,
61 										 const char *langMarker,
62 										 vString *langName)
63 {
64 	const char *cp = langMarker;
65 
66 	if (*cp != '{')
67 		return false;
68 	cp++;
69 
70 	const char *end = strpbrk(cp, " \t,}");
71 	if (!end)
72 		return false;
73 
74 	if (end - cp == 0)
75 		return false;
76 
77 	vStringNCatS (langName, cp, end - cp);
78 
79 	cp = end;
80 	if (*cp == ',' || *cp == '}')
81 	{
82 		vString *name = anonGenerateNew("__anon", K_CHUNK_LABEL);
83 		makeRMarkdownTag (name, K_CHUNK_LABEL, true);
84 		vStringDelete (name);
85 		return true;
86 	}
87 
88 	skip_space(cp);
89 
90 	vString *chunk_label  = vStringNew ();
91 	bool anonymous = false;
92 	while (isalnum((unsigned char)*cp) || *cp == '-')
93 		vStringPut (chunk_label, *cp++);
94 
95 	if (vStringLength (chunk_label) == 0)
96 	{
97 		anonGenerate (chunk_label, "__anon", K_CHUNK_LABEL);
98 		anonymous = true;
99 	}
100 
101 	skip_space(cp);
102 	if (*cp == ',' || *cp == '}')
103 		makeRMarkdownTag (chunk_label, K_CHUNK_LABEL, anonymous);
104 
105 	vStringDelete (chunk_label);
106 	return true;
107 }
108 
RMarkdownParser(void)109 extern parserDefinition* RMarkdownParser (void)
110 {
111 	static const char *const extensions [] = { "rmd", NULL };
112 	static struct sRMarkdownSubparser rmarkdownSubparser = {
113 		.markdown = {
114 			.subparser = {
115 				.direction = SUBPARSER_SUB_RUNS_BASE,
116 			},
117 			.extractLanguageForCodeBlock = extractLanguageForCodeBlock,
118 		},
119 	};
120 	static parserDependency dependencies [] = {
121 		[0] = { DEPTYPE_SUBPARSER, "Markdown", &rmarkdownSubparser },
122 	};
123 
124 	parserDefinition* const def = parserNew ("RMarkdown");
125 
126 
127 	def->dependencies = dependencies;
128 	def->dependencyCount = ARRAY_SIZE(dependencies);
129 	def->kindTable      = RMarkdownKinds;
130 	def->kindCount  = ARRAY_SIZE (RMarkdownKinds);
131 	def->extensions = extensions;
132 	def->parser     = findRMarkdownTags;
133 	return def;
134 }
135