xref: /Universal-ctags/peg/elm_post.h (revision 94e964efcdb54004e666334f92f6bee597c7ab96)
1 /*
2  *   Copyright (c) 2022 Nik Silver
3  *   based on the Kotlin code by Jan Dolinár
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 to generate tags for Elm.
9  *   See https://elm-lang.org/docs/syntax for language reference.
10  */
11 
12 /*
13  *   INCLUDE FILES
14  */
15 #include "parse.h"
16 #include "trace.h"
17 
18 /*
19  * FUNCTION DEFINITIONS
20  */
21 
22 /* kind - The kind of this tag.
23  * role - The role this tag plays. But def(ined) is expressed by setting
24  *     this to ROLE_DEFINITION_INDEX.
25  * pushScope - If true, also update the scope to be this tag.
26  */
makeElmTag(struct parserCtx * auxil,const char * name,long offset,int kind,int role)27 static int makeElmTag (struct parserCtx *auxil, const char *name, long offset, int kind, int role)
28 {
29 	tagEntryInfo e;
30 
31 	if (role == ROLE_DEFINITION_INDEX)
32 	{
33 		initTagEntry (&e, name, kind);
34 	}
35 	else
36 	{
37 		initRefTagEntry (&e, name, kind, role);
38 	}
39 
40 	e.lineNumber = getInputLineNumberForFileOffset (offset);
41 	e.filePosition = getInputFilePositionForLine (e.lineNumber);
42 	e.extensionFields.scopeIndex = BASE_SCOPE (auxil);
43 	int scope_index = makeTagEntry (&e);
44 	return scope_index;
45 }
46 
47 /* Like makeElmTag, but have this tag be the latest scope.
48  */
makeElmTagSettingScope(struct parserCtx * auxil,const char * name,long offset,int kind,int role)49 static int makeElmTagSettingScope (struct parserCtx *auxil, const char *name, long offset, int kind, int role)
50 {
51 	// Here is a useless line just to use a function and so
52 	// get rid of a compiler warning.
53 	if (1 < 0)
54 	{
55 		PEEK_KIND (auxil);
56 	}
57 	// The real function starts here...
58 
59 	int scope_index = makeElmTag (auxil, name, offset, kind, role);
60 	SET_SCOPE (auxil, scope_index);
61 	return scope_index;
62 }
63 
addElmSignature(int scope_index,const char * sig)64 static void addElmSignature(int scope_index, const char *sig)
65 {
66 	tagEntryInfo *e = getEntryInCorkQueue (scope_index);
67 
68 	if (e)
69 	{
70 		vString *vsig = collapseWhitespace (sig);
71 
72 		e->extensionFields.signature = vStringDeleteUnwrap (vsig);
73 	}
74 }
75 
addElmTypeRef(int scope_index,const char * sig)76 static void addElmTypeRef(int scope_index, const char *sig)
77 {
78 	tagEntryInfo *e = getEntryInCorkQueue (scope_index);
79 
80 	if (e)
81 	{
82 		vString *vsig = collapseWhitespace (sig);
83 
84 		e->extensionFields.typeRef [0] = eStrdup ("typename");
85 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vsig);
86 	}
87 }
88 
89 /* There are several steps to making the type of constructors within
90  * a custom type:
91  *   1. Initialise the fields when we encounter the custom type declaration.
92  *   2. Initialise the subtype field (do this for each new constructor).
93  *   3. Add each subtype as we find it.
94  *   4. Make the typeref field.
95  *   5. Tidy up
96  */
initElmConstructorFields(struct parserCtx * auxil,const char * name)97 static void initElmConstructorFields (struct parserCtx *auxil, const char *name)
98 {
99 	auxil->customType = vStringNewInit (name);
100 	auxil->consSubtype = vStringNew ();
101 }
102 
initElmConstructorSubtypeFields(struct parserCtx * auxil)103 static void initElmConstructorSubtypeFields (struct parserCtx *auxil)
104 {
105 	vStringClear (auxil->consSubtype);
106 }
107 
addElmConstructorSubtype(struct parserCtx * auxil,const char * name)108 static void addElmConstructorSubtype (struct parserCtx *auxil, const char *name)
109 {
110 	vStringCatS (auxil->consSubtype, name);
111 	vStringCatS (auxil->consSubtype, " -> ");
112 }
113 
addElmConstructorTypeRef(struct parserCtx * auxil,int tag_index)114 static void addElmConstructorTypeRef (struct parserCtx *auxil, int tag_index)
115 {
116 	vStringCat (auxil->consSubtype, auxil->customType);
117 	addElmTypeRef (tag_index, vStringValue (auxil->consSubtype));
118 }
119 
tidyElmConstructorFields(struct parserCtx * auxil)120 static void tidyElmConstructorFields (struct parserCtx *auxil)
121 {
122 	vStringDelete (auxil->consSubtype);
123 	vStringDelete (auxil->customType);
124 }
125 
126 /* For a signature such as "a1   b2  c3" we want to transform it
127  * to "a1 b2 c3" for the signature field.
128  */
collapseWhitespace(const char * sig)129 static vString *collapseWhitespace (const char *sig)
130 {
131 	vString *vstr = vStringNew ();
132 
133 	const char *c = sig;
134 	char c2;
135 	int found_ws = 0;
136 
137 	for ( ; *c != '\0'; c++)
138 	{
139 		// The character, in case we need to change it
140 		c2 = *c;
141 
142 		if (isspace ((unsigned char) c2))
143 		{
144 			// It's whitespace. Make it plain space
145 			c2 = ' ';
146 			if (found_ws)
147 			{
148 				// We found whitespace just before, so ignore this
149 				continue;
150 			}
151 
152 			found_ws = 1;
153 		}
154 		else
155 		{
156 			found_ws = 0;
157 		}
158 		vStringPut (vstr, c2);
159 	}
160 
161 	return vstr;
162 }
163 
ctxInit(struct parserCtx * auxil)164 static void ctxInit (struct parserCtx *auxil)
165 {
166 	BASE_INIT (auxil, K_MODULE);
167 }
168 
ctxFini(struct parserCtx * auxil)169 static void ctxFini (struct parserCtx *auxil)
170 {
171 	BASE_FINI (auxil);
172 }
173 
findElmTags(void)174 static void findElmTags (void)
175 {
176 	struct parserCtx auxil;
177 
178 	ctxInit (&auxil);
179 	pelm_context_t *pctx = pelm_create (&auxil);
180 
181 	while (pelm_parse (pctx, NULL) && (! BASE_ERROR (&auxil)))
182 		;
183 
184 	pelm_destroy (pctx);
185 	ctxFini (&auxil);
186 }
187 
ElmParser(void)188 extern parserDefinition *ElmParser (void)
189 {
190 	static const char *const extensions [] = { "elm", NULL };
191 	parserDefinition *def = parserNew ("Elm");
192 	def->kindTable = ElmKinds;
193 	def->kindCount = ARRAY_SIZE (ElmKinds);
194 	def->extensions = extensions;
195 	def->parser = findElmTags;
196 	def->fieldTable = ElmFields;
197 	def->fieldCount = ARRAY_SIZE (ElmFields);
198 	def->useCork = true;
199 	def->enabled    = true;
200 	//def->requestAutomaticFQTag = true;
201 	def->defaultScopeSeparator = ".";
202 	return def;
203 }
204