Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Jiří Techet <techet(a)gmail.com>
Date: Fri, 13 May 2022 00:39:12 UTC
Commit: 2ed8955886b142a44e6bb69dc30f75d4bd02063b
https://github.com/geany/geany/commit/2ed8955886b142a44e6bb69dc30f75d4bd020…
Log Message:
-----------
Add ADA ctags parser
Modified Paths:
--------------
ctags/Makefile.am
ctags/parsers/ada.c
meson.build
src/filetypes.c
src/tagmanager/tm_parser.c
src/tagmanager/tm_parser.h
src/tagmanager/tm_parsers.h
tests/ctags/Makefile.am
tests/ctags/ada-adb.adb
tests/ctags/ada-adb.adb.tags
tests/ctags/ada-ads.ads
tests/ctags/ada-ads.ads.tags
tests/ctags/ada-expression-function-generic.adb
tests/ctags/ada-expression-function-generic.adb.tags
tests/ctags/ada-expression-function.adb
tests/ctags/ada-expression-function.adb.tags
tests/ctags/ada-overriding.ads
tests/ctags/ada-overriding.ads.tags
tests/ctags/ada-package-generic.ads
tests/ctags/ada-package-generic.ads.tags
tests/ctags/ada-task.adb
tests/ctags/ada-task.adb.tags
tests/meson.build
Modified: ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -45,6 +45,7 @@ parsers = \
parsers/cpreprocessor.h \
parsers/abaqus.c \
parsers/abc.c \
+ parsers/ada.c \
parsers/asciidoc.c \
parsers/asm.c \
parsers/basic.c \
Modified: ctags/parsers/ada.c
2395 lines changed, 2395 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,2395 @@
+/* File: Ada.c
+ * Description: Enables extended Ada parsing support in Exuberant Ctags
+ * Version: 0.6
+ * Date: October 26, 2006
+ * Author: A. Aaron Cornelius (ADotAaronDotCorneliusAtgmailDotcom)
+ * License: GPL-2
+ *
+ * Installation:
+ * You must have the Exuberant Ctags source to install this parser. Once you
+ * have the source, place this file into the directory with the rest of the
+ * ctags source. After ada.c is in the correct directory you need to make the
+ * following changes so that the ada parser is included when you compile and
+ * install ctags:
+ *
+ * to file source.mak add the line
+ * ada.c \
+ *
+ * after
+ * SOURCES = \
+ *
+ * then add the line
+ * ada.$(OBJECT) \
+ *
+ * after
+ * OBJECTS = \
+ *
+ * to file parsers.h add the line
+ * AdaParser, \
+ *
+ * after
+ * #define PARSER_LIST \
+ *
+ * Then compile and install ctags as normal (usually: './configure', './make',
+ * './make install').
+ *
+ * Changelog:
+ *
+ * 11/02/2006 - Completed implementation of file scope info and qualified tags
+ * information gathering.
+ * 11/02/2006 - Added recognition of private flag in a token for file scope
+ * checking purposes.
+ * 10/27/2006 - Added full package scope name when --extra=+q is set.
+ * 10/27/2006 - Fixed file scope setting, and added check to verify that tags
+ * with file scope should be included in the tag file.
+ * 10/26/2006 - Fixed error which caused infinite loop when parsing some
+ * files.
+ * 0.5 - Bugfixes
+ * 10/20/2006 - Cleaned up freeAdaTokenList.
+ * 10/20/2006 - Fixed error in freeAdaToken that caused the child token lists
+ * to become corrupted when "separate" tokens were deleted.
+ * 0.4 - Third Revision - 09/25/2006
+ * 09/25/2006 - Fixed error in newAdaToken which could cause an error on some
+ * systems when a separate token (which is temporary) gets
+ * created.
+ * 09/25/2006 - Change matchFilePos initialization in the findAdaTags
+ * function.
+ * 0.3 - Second Revision
+ * 06/02/2006 - Added missing EOF checks to prevent infinite loops in the case
+ * of an incomplete Ada (or non-Ada) file being parsed.
+ * 06/02/2006 - Added Copyright notice.
+ * 0.2 - First Revision
+ * 05/26/2006 - Fixed an error where tagging the proper scope of something
+ * declared in an anonymous block or anonymous loop was not
+ * working properly.
+ * 05/26/2006 - Fixed an error capturing the name of a 'separate' tag.
+ * 05/26/2006 - Fixed the cmp() function so that it finds matches correctly.
+ * 05/26/2006 - Fixed some spelling errors.
+ * 05/26/2006 - Added explicit skipping of use and with clauses.
+ * 0.1 - Initial Release
+ *
+ * Future Changes:
+ * TODO: Add inheritance information?
+ * TODO: Add signature gathering?
+ *
+ * Copyright (C) 2006 A. Aaron Cornelius
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ */
+
+#include "general.h" /* always include first */
+
+#include <string.h> /* to declare strxxx() functions */
+#include <ctype.h> /* to define isxxx() macros */
+
+#include "parse.h" /* always include */
+#include "read.h" /* to define file readLineFromInputFile() */
+#include "entry.h" /* for the tag entry manipulation */
+#include "routines.h" /* for generic malloc/realloc/free routines */
+#include "debug.h" /* for Assert */
+#include "xtag.h"
+
+
+static bool eof_reached;
+
+typedef enum eAdaParseMode
+{
+ ADA_ROOT,
+ ADA_DECLARATIONS,
+ ADA_CODE,
+ ADA_EXCEPTIONS,
+ ADA_GENERIC
+} adaParseMode;
+
+typedef enum eAdaKinds
+{
+ ADA_KIND_UNDEFINED = KIND_GHOST_INDEX, /* for default/initialization values */
+ ADA_KIND_PACKAGE_SPEC,
+ ADA_KIND_PACKAGE,
+ ADA_KIND_TYPE_SPEC,
+ ADA_KIND_TYPE,
+ ADA_KIND_SUBTYPE_SPEC,
+ ADA_KIND_SUBTYPE,
+ ADA_KIND_RECORD_COMPONENT,
+ ADA_KIND_ENUM_LITERAL,
+ ADA_KIND_VARIABLE_SPEC,
+ ADA_KIND_VARIABLE,
+ ADA_KIND_FORMAL,
+ ADA_KIND_CONSTANT,
+ ADA_KIND_EXCEPTION,
+ ADA_KIND_SUBPROGRAM_SPEC,
+ ADA_KIND_SUBPROGRAM,
+ ADA_KIND_TASK_SPEC,
+ ADA_KIND_TASK,
+ ADA_KIND_PROTECTED_SPEC,
+ ADA_KIND_PROTECTED,
+ ADA_KIND_ENTRY_SPEC,
+ ADA_KIND_ENTRY,
+ ADA_KIND_LABEL,
+ ADA_KIND_IDENTIFIER,
+ ADA_KIND_AUTOMATIC_VARIABLE,
+ ADA_KIND_ANONYMOUS, /* for non-identified loops and blocks */
+ ADA_KIND_COUNT /* must be last */
+} adaKind;
+
+typedef enum {
+ ADA_PACKAGE_SUBUNIT,
+} adaPackageRole;
+
+static roleDefinition AdaPackageRoles [] = {
+ { true, "subunit",
+ "package name referenced in separate()" },
+};
+
+static kindDefinition AdaKinds[] =
+{
+ { true, 'P', "packspec", "package specifications" },
+ { true, 'p', "package", "packages",
+ .referenceOnly = false, ATTACH_ROLES(AdaPackageRoles) },
+ { false, 'T', "typespec", "type specifications" },
+ { true, 't', "type", "types" },
+ { false, 'U', "subspec", "subtype specifications" },
+ { true, 'u', "subtype", "subtypes" },
+ { true, 'c', "component", "record type components" },
+ { true, 'l', "literal", "enum type literals" },
+ { false, 'V', "varspec", "variable specifications" },
+ { true, 'v', "variable", "variables" },
+ { true, 'f', "formal", "generic formal parameters" },
+ { true, 'n', "constant", "constants" },
+ { true, 'x', "exception", "user defined exceptions" },
+ { true, 'R', "subprogspec", "subprogram specifications" },
+ { true, 'r', "subprogram", "subprograms" },
+ { true, 'K', "taskspec", "task specifications" },
+ { true, 'k', "task", "tasks" },
+ { true, 'O', "protectspec", "protected data specifications" },
+ { true, 'o', "protected", "protected data" },
+ { false, 'E', "entryspec", "task/protected data entry specifications" },
+ { true, 'e', "entry", "task/protected data entries" },
+ { true, 'b', "label", "labels" },
+ { true, 'i', "identifier", "loop/declare identifiers"},
+ { false, 'a', "autovar", "automatic variables" },
+ { false, 'y', "anon", "loops and blocks with no identifier" },
+};
+
+typedef struct sAdaTokenList
+{
+ int numTokens;
+ struct sAdaTokenInfo *head;
+ struct sAdaTokenInfo *tail;
+} adaTokenList;
+
+typedef struct sAdaTokenInfo
+{
+ adaKind kind;
+ bool isSpec;
+ bool isPrivate;
+ char *name;
+ tagEntryInfo tag;
+ struct sAdaTokenInfo *parent;
+ struct sAdaTokenInfo *prev;
+ struct sAdaTokenInfo *next;
+ adaTokenList children;
+} adaTokenInfo;
+
+typedef enum eAdaKeywords
+{
+ ADA_KEYWORD_ACCEPT,
+ ADA_KEYWORD_BEGIN,
+ ADA_KEYWORD_BODY,
+ ADA_KEYWORD_CASE,
+ ADA_KEYWORD_CONSTANT,
+ ADA_KEYWORD_DECLARE,
+ ADA_KEYWORD_DO,
+ ADA_KEYWORD_ELSE,
+ ADA_KEYWORD_ELSIF,
+ ADA_KEYWORD_END,
+ ADA_KEYWORD_ENTRY,
+ ADA_KEYWORD_EXCEPTION,
+ ADA_KEYWORD_FOR,
+ ADA_KEYWORD_FUNCTION,
+ ADA_KEYWORD_GENERIC,
+ ADA_KEYWORD_IF,
+ ADA_KEYWORD_IN,
+ ADA_KEYWORD_IS,
+ ADA_KEYWORD_LOOP,
+ ADA_KEYWORD_NEW,
+ ADA_KEYWORD_NOT,
+ ADA_KEYWORD_OR,
+ ADA_KEYWORD_OVERRIDING, /* Ada 2005 */
+ ADA_KEYWORD_PACKAGE,
+ ADA_KEYWORD_PRAGMA,
+ ADA_KEYWORD_PRIVATE,
+ ADA_KEYWORD_PROCEDURE,
+ ADA_KEYWORD_PROTECTED,
+ ADA_KEYWORD_RECORD,
+ ADA_KEYWORD_RENAMES,
+ ADA_KEYWORD_SELECT,
+ ADA_KEYWORD_SEPARATE,
+ ADA_KEYWORD_SUBTYPE,
+ ADA_KEYWORD_TASK,
+ ADA_KEYWORD_THEN,
+ ADA_KEYWORD_TYPE,
+ ADA_KEYWORD_UNTIL,
+ ADA_KEYWORD_USE,
+ ADA_KEYWORD_WHEN,
+ ADA_KEYWORD_WHILE,
+ ADA_KEYWORD_WITH
+} adaKeyword;
+
+static const char *AdaKeywords[] =
+{
+ "accept",
+ "begin",
+ "body",
+ "case",
+ "constant",
+ "declare",
+ "do",
+ "else",
+ "elsif",
+ "end",
+ "entry",
+ "exception",
+ "for",
+ "function",
+ "generic",
+ "if",
+ "in",
+ "is",
+ "loop",
+ "new",
+ "not",
+ "or",
+ "overriding",
+ "package",
+ "pragma",
+ "private",
+ "procedure",
+ "protected",
+ "record",
+ "renames",
+ "select",
+ "separate",
+ "subtype",
+ "task",
+ "then",
+ "type",
+ "until",
+ "use",
+ "when",
+ "while",
+ "with"
+};
+
+
+/* variables for managing the input string, position as well as input line
+ * number and position */
+static const char *line;
+static int lineLen;
+static int pos;
+static unsigned long matchLineNum;
+static MIOPos matchFilePos;
+
+/* utility functions */
+static void makeSpec (adaKind *kind);
+
+/* prototypes of functions for manipulating the Ada tokens */
+static adaTokenInfo *newAdaToken (const char *name, int len,
+ adaKind kind, bool isSpec,
+ adaTokenInfo *parent);
+static adaTokenInfo *newAdaTokenFull (const char *name, int len,
+ adaKind kind, int role, bool isSpec,
+ adaTokenInfo *parent);
+static void freeAdaToken (adaTokenList *list, adaTokenInfo *token);
+static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token);
+
+/* token list processing function prototypes */
+static void initAdaTokenList (adaTokenList *list);
+static void freeAdaTokenList (adaTokenList *list);
+static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children);
+
+/* prototypes of functions for moving through the DEFINED text */
+static void readNewLine (void);
+static void movePos (int amount);
+static bool cmp (const char *buf, int len, const char *match);
+static bool adaCmp (const char *match);
+static bool adaKeywordCmp (adaKeyword keyword);
+static void skipUntilWhiteSpace (void);
+static void skipWhiteSpace (void);
+static void skipComments (void);
+static void skipCommentsAndStringLiteral (void);
+static void skipPast (const char *past);
+static void skipPastKeyword (adaKeyword keyword);
+static void skipPastWord (void);
+
+typedef bool (* skipCompFn) (void *data);
+static void skipPastLambda (skipCompFn cmpfn, void *data);
+
+struct cmpKeywordOrWordDataElt
+{
+ enum eltType
+ {
+ ELT_KEYWORD,
+ ELT_WORD,
+ } type;
+ union
+ {
+ adaKeyword keyword;
+ const char* word;
+ } u;
+};
+static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
+ int count);
+
+/* prototypes of functions for parsing the high-level Ada constructs */
+static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent);
+static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent);
+
+/* prototypes of the functions used by ctags */
+static void storeAdaTags (adaTokenInfo *token, const char *parentScope);
+static void findAdaTags (void);
+
+static void makeSpec (adaKind *kind)
+{
+ switch (*kind)
+ {
+ case ADA_KIND_PACKAGE:
+ *kind = ADA_KIND_PACKAGE_SPEC;
+ break;
+
+ case ADA_KIND_TYPE:
+ *kind = ADA_KIND_TYPE_SPEC;
+ break;
+
+ case ADA_KIND_SUBTYPE:
+ *kind = ADA_KIND_SUBTYPE_SPEC;
+ break;
+
+ case ADA_KIND_VARIABLE:
+ *kind = ADA_KIND_VARIABLE_SPEC;
+ break;
+
+ case ADA_KIND_SUBPROGRAM:
+ *kind = ADA_KIND_SUBPROGRAM_SPEC;
+ break;
+
+ case ADA_KIND_TASK:
+ *kind = ADA_KIND_TASK_SPEC;
+ break;
+
+ case ADA_KIND_PROTECTED:
+ *kind = ADA_KIND_PROTECTED_SPEC;
+ break;
+
+ case ADA_KIND_ENTRY:
+ *kind = ADA_KIND_ENTRY_SPEC;
+ break;
+
+ default:
+ *kind = ADA_KIND_UNDEFINED;
+ break;
+ }
+}
+
+static adaTokenInfo *newAdaTokenFull (const char *name, int len, adaKind kind, int role,
+ bool isSpec, adaTokenInfo *parent)
+{
+ char *tmpName = NULL;
+ adaTokenInfo *token = xMalloc (1, adaTokenInfo);
+
+ token->name = NULL;
+
+ if (name != NULL && len != 0)
+ {
+ tmpName = xMalloc (len + 1, char);
+ strncpy (tmpName, name, len);
+ tmpName[len] = '\0';
+ }
+
+ /* init the tag */
+ initTagEntry (&token->tag, tmpName, ADA_KIND_UNDEFINED);
+
+ token->kind = kind;
+ token->isSpec = isSpec;
+ token->isPrivate = false;
+
+ /* set the token data */
+ token->name = tmpName;
+ token->parent = parent;
+
+ /* Now set the file scope for this tag. A tag has file scope if its direct
+ * parent is a package/subprogram/protected/task spec, or if it it's parent
+ * is UNDEFINED (a 'root' token), and if this is not in a 'private' section
+ * of that spec. */
+ if ((parent != NULL) && (parent->isPrivate == false) &&
+ ((parent->kind == ADA_KIND_UNDEFINED) ||
+ (parent->kind == ADA_KIND_PACKAGE && isRoleAssigned(&parent->tag, ADA_PACKAGE_SUBUNIT)) ||
+ ((parent->isSpec == true) && ((parent->kind == ADA_KIND_PACKAGE) ||
+ (parent->kind == ADA_KIND_SUBPROGRAM) ||
+ (parent->kind == ADA_KIND_PROTECTED) ||
+ (parent->kind == ADA_KIND_TASK)))))
+ {
+ token->tag.isFileScope = false;
+ }
+ else
+ {
+ markTagExtraBit (&token->tag, XTAG_FILE_SCOPE);
+ token->tag.isFileScope = true;
+ }
+
+ /* add the kind info - unless this is a SEPARATE kind, in which case keep
+ * them blank because they get filled in later. */
+ if (kind > ADA_KIND_UNDEFINED)
+ {
+ token->tag.kindIndex = kind;
+ if (role != ROLE_DEFINITION_INDEX)
+ assignRole(&token->tag, role);
+ }
+ else
+ {
+ token->tag.kindIndex = KIND_GHOST_INDEX;
+ }
+
+ /* setup the parent and children pointers */
+ initAdaTokenList (&token->children);
+ appendAdaToken (parent, token);
+
+ return token;
+}
+
+static adaTokenInfo *newAdaToken (const char *name, int len, adaKind kind,
+ bool isSpec, adaTokenInfo *parent)
+{
+ return newAdaTokenFull (name, len, kind, ROLE_DEFINITION_INDEX, isSpec, parent);
+}
+
+static void freeAdaToken (adaTokenList *list, adaTokenInfo *token)
+{
+ if (token != NULL)
+ {
+ if (token->name != NULL)
+ {
+ eFree ((void *) token->name);
+ token->name = NULL;
+ }
+
+ /* before we delete this token, clean up it's children */
+ freeAdaTokenList (&token->children);
+
+ /* move the next token in the list to this token's spot */
+ if (token->prev != NULL)
+ {
+ token->prev->next = token->next;
+ }
+ else if (list != NULL)
+ {
+ /* Token to remove is head in list as 'token->prev == NULL' */
+ Assert (token->prev == NULL);
+ list->head = token->next;
+ }
+
+ /* move the previous token in the list to this token's spot */
+ if (token->next != NULL)
+ {
+ token->next->prev = token->prev;
+ }
+ else if (list != NULL)
+ {
+ /* Token to remove is tail of list as 'token->next == NULL') */
+ Assert (token->next == NULL);
+ list->tail = token->prev;
+ }
+
+ /* decrement the list count */
+ if (list != NULL)
+ {
+ list->numTokens--;
+ }
+
+ /* now that this node has had everything hanging off of it rearranged,
+ * delete this node */
+ eFree (token);
+ }
+}
+
+static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token)
+{
+ /* if the parent or newChild is NULL there is nothing to be done */
+ if (parent != NULL && token != NULL)
+ {
+ /* we just need to add this to the list and set a parent pointer */
+ parent->children.numTokens++;
+ token->parent = parent;
+ token->prev = parent->children.tail;
+ token->next = NULL;
+
+ if (parent->children.tail != NULL)
+ {
+ parent->children.tail->next = token;
+ }
+
+ /* the token that was just added always becomes the last token int the
+ * list */
+ parent->children.tail = token;
+
+ if (parent->children.head == NULL)
+ {
+ parent->children.head = token;
+ }
+ }
+}
+
+static void initAdaTokenList (adaTokenList *list)
+{
+ if (list != NULL)
+ {
+ list->numTokens = 0;
+ list->head = NULL;
+ list->tail = NULL;
+ }
+}
+
+static void freeAdaTokenList (adaTokenList *list)
+{
+ if (list != NULL)
+ {
+ while (list->head != NULL)
+ {
+ freeAdaToken (list, list->head);
+ }
+ }
+}
+
+static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children)
+{
+ adaTokenInfo *tmp = NULL;
+
+ if (parent != NULL && children != NULL)
+ {
+ while (children->head != NULL)
+ {
+ tmp = children->head->next;
+ appendAdaToken (parent, children->head);
+
+ /* we just need to worry about setting the head pointer properly during
+ * the list iteration. The node's pointers will get set properly by the
+ * appendAdaToken () function */
+ children->head = tmp;
+ }
+
+ /* now that we have added all nodes from the children list to the parent
+ * node, zero out the children list */
+ initAdaTokenList (children);
+ }
+}
+
+static void readNewLine (void)
+{
+ while (true)
+ {
+ line = (const char *) readLineFromInputFile ();
+ pos = 0;
+
+ if (line == NULL)
+ {
+ lineLen = 0;
+ eof_reached = true;
+ return;
+ }
+
+ lineLen = strlen (line);
+
+ if (lineLen > 0)
+ {
+ return;
+ }
+ }
+}
+
+static void movePos (int amount)
+{
+ pos += amount;
+ if (!eof_reached && pos >= lineLen)
+ {
+ readNewLine ();
+ }
+}
+
+/* a macro for checking for comments... This isn't the same as the check in
+ * cmp () because comments don't have to have whitespace or separation-type
+ * characters following the "--" */
+#define isAdaComment(buf, pos, len) \
+ (((pos) == 0 || (!isalnum ((buf)[(pos) - 1]) && (buf)[(pos) - 1] != '_')) && \
+ (pos) < (len) && \
+ strncasecmp (&(buf)[(pos)], "--", strlen ("--")) == 0)
+#define isAdaStringLiteral(buf, pos, len) \
+ (((pos) < (len)) && ((buf)[(pos)] == '"'))
+#define isAdaCharLiteral(buf, pos, len) \
+ (((pos) < (len - 2)) && ((buf)[(pos)] == '\'') \
+ && ((buf)[(pos + 2)] == '\''))
+
+
+static bool cmp (const char *buf, int len, const char *match)
+{
+ bool status = false;
+ int matchLen;
+
+ /* if we are trying to match nothing, that is always true */
+ if (match == NULL)
+ {
+ return true;
+ }
+
+ /* first check to see if the buffer is empty, if it is, return false */
+ if (buf == NULL)
+ {
+ return status;
+ }
+
+ matchLen = strlen (match);
+
+ /* A match only happens the number of chars in the matching string match,
+ * and whitespace follows... Which means we also must check to see if the
+ * end of the line is after the matching string. Also check for some
+ * separation characters such as (, ), :, or ; */
+ if ((strncasecmp (buf, match, matchLen) == 0) &&
+ (matchLen == len ||
+ (matchLen < len &&
+ (isspace (buf[matchLen]) || buf[matchLen] == '(' ||
+ buf[matchLen] == ')' || buf[matchLen] == ':' ||
+ buf[matchLen] == ';'))))
+ {
+ status = true;
+ }
+
+ return status;
+}
+
+static bool adaCmp (const char *match)
+{
+ bool status = false;
+
+ /* first check to see if line is empty */
+ if (line == NULL)
+ {
+ eof_reached = true;
+ return status;
+ }
+
+ status = cmp (&line[pos], lineLen - pos, match);
+
+ /* if we match, increment the position pointer */
+ if (status == true && match != NULL)
+ {
+ matchLineNum = getInputLineNumber ();
+ matchFilePos = getInputFilePosition ();
+
+ movePos ((strlen (match)));
+ }
+
+ return status;
+}
+
+/* just a version of adaCmp that is a bit more optimized for keywords */
+static bool adaKeywordCmp (adaKeyword keyword)
+{
+ bool status = false;
+
+ /* first check to see if line is empty, if it is */
+ if (line == NULL)
+ {
+ eof_reached = true;
+ return status;
+ }
+
+ status = cmp (&line[pos], lineLen - pos, AdaKeywords[keyword]);
+
+ /* if we match, increment the position pointer */
+ if (status == true)
+ {
+ matchLineNum = getInputLineNumber ();
+ matchFilePos = getInputFilePosition ();
+
+ movePos ((strlen (AdaKeywords[keyword])));
+ }
+
+ return status;
+}
+
+static void skipUntilWhiteSpace (void)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to be true immediately */
+ skipComments ();
+
+ while (!eof_reached && !isspace (line[pos]))
+ {
+ /* don't use movePos () because if we read in a new line with this function
+ * we need to stop */
+ pos++;
+
+ /* the newline counts as whitespace so read in the newline and return
+ * immediately */
+ if (pos >= lineLen)
+ {
+ line = (const char *) readLineFromInputFile ();
+ pos = 0;
+
+ if (line == NULL)
+ {
+ lineLen = 0;
+ eof_reached = true;
+ return;
+ }
+
+ lineLen = strlen (line);
+
+ return;
+ }
+
+ /* now check for comments here */
+ skipComments ();
+ }
+}
+
+static void skipWhiteSpace (void)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to fail immediately */
+ skipComments ();
+
+ while (!eof_reached && isspace (line[pos]))
+ {
+ movePos (1);
+
+ /* now check for comments here */
+ skipComments ();
+ }
+}
+
+static void skipComments (void)
+{
+ while (!eof_reached && isAdaComment (line, pos, lineLen))
+ {
+ readNewLine ();
+ }
+}
+
+/* Return true if skipped over a string literal (or char literal).
+ * Return false if no string literal (nor char literal) is found. */
+static bool skipStringLiteral (void)
+{
+ if (!eof_reached && isAdaStringLiteral (line, pos, lineLen))
+ {
+ do {
+ movePos (1);
+ } while (!eof_reached && !isAdaStringLiteral (line, pos, lineLen));
+
+ /* Go to the next char of " */
+ movePos (1);
+
+ return true;
+ }
+ else if (!eof_reached && isAdaCharLiteral (line, pos, lineLen))
+ {
+ movePos (3);
+ return true;
+ }
+ return false;
+}
+
+static void skipCommentsAndStringLiteral (void)
+{
+ while (true)
+ {
+ skipComments ();
+ if (!skipStringLiteral ())
+ break;
+ }
+}
+
+static void skipPast (const char *past)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to fail immediately */
+ skipCommentsAndStringLiteral ();
+
+ /* now look for the keyword */
+ while (!eof_reached && !adaCmp (past))
+ {
+ movePos (1);
+
+ /* now check for comments here */
+ skipCommentsAndStringLiteral ();
+ }
+}
+
+static void skipPastKeyword (adaKeyword keyword)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to fail immediately */
+ skipComments ();
+
+ /* now look for the keyword */
+ while (!eof_reached && !adaKeywordCmp (keyword))
+ {
+ movePos (1);
+
+ /* now check for comments here */
+ skipComments ();
+ }
+}
+
+static void skipPastWord (void)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to fail immediately */
+ skipComments ();
+
+ /* now increment until we hit a non-word character... Specifically,
+ * whitespace, '(', ')', ':', and ';' */
+ while (!eof_reached && !isspace (line[pos]) &&
+ line[pos] != '(' && line[pos] != ')' && line[pos] != ':' &&
+ line[pos] != ';')
+ {
+ /* don't use movePos () because if we read in a new line with this function
+ * we need to stop */
+ pos++;
+
+ /* the newline counts as whitespace so read in the newline and return
+ * immediately */
+ if (pos >= lineLen)
+ {
+ line = (const char *) readLineFromInputFile ();
+ pos = 0;
+
+ if (line == NULL)
+ {
+ lineLen = 0;
+ eof_reached = true;
+ return;
+ }
+
+ lineLen = strlen (line);
+
+ return;
+ }
+
+ /* now check for comments here */
+ skipComments ();
+ }
+}
+
+static void skipPastLambda (skipCompFn cmpfn, void *data)
+{
+ /* first check for a comment line, because this would cause the isspace
+ * check to fail immediately */
+ skipCommentsAndStringLiteral ();
+
+ /* now call the predicate */
+ while (!eof_reached && !cmpfn (data))
+ {
+ movePos (1);
+
+ /* now check for comments here */
+ skipCommentsAndStringLiteral ();
+ }
+}
+
+struct cmpKeywordOrWordData
+{
+ struct cmpKeywordOrWordDataElt *found;
+ int count;
+ struct cmpKeywordOrWordDataElt *elt;
+};
+
+static bool cmpKeywordOrWord (void *data)
+{
+ struct cmpKeywordOrWordData *cmdData = data;
+
+ cmdData->found = NULL;
+ for (int i = 0; i < cmdData->count; i++)
+ {
+ if (cmdData->elt[i].type == ELT_KEYWORD)
+ {
+ if (adaKeywordCmp (cmdData->elt[i].u.keyword))
+ {
+ cmdData->found = cmdData->elt + i;
+ return true;
+ }
+ }
+ else if (cmdData->elt[i].type == ELT_WORD)
+ {
+ if (adaCmp (cmdData->elt[i].u.word))
+ {
+ cmdData->found = cmdData->elt + i;
+ return true;
+ }
+ }
+ else
+ AssertNotReached ();
+ }
+ return false;
+}
+
+static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
+ int count)
+{
+ struct cmpKeywordOrWordData data = {
+ .found = NULL,
+ .count = count,
+ .elt = elt
+ };
+ skipPastLambda (cmpKeywordOrWord, &data);
+ return data.found;
+}
+
+static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind)
+{
+ int i;
+ adaTokenInfo *token;
+ bool isSpec = true;
+
+ skipWhiteSpace ();
+
+ /* if the next word is body, this is not a package spec */
+ if (adaKeywordCmp (ADA_KEYWORD_BODY))
+ {
+ isSpec = false;
+ }
+ /* if the next word is "type" then this has to be a task or protected spec */
+ else if (adaKeywordCmp (ADA_KEYWORD_TYPE) &&
+ (kind != ADA_KIND_PROTECTED && kind != ADA_KIND_TASK))
+ {
+ /* if this failed to validate then we should just fail */
+ return NULL;
+ }
+ skipWhiteSpace ();
+
+ /* we are at the start of what should be the tag now... But we have to get
+ * it's length. So loop until we hit whitespace, init the counter to 1
+ * since we know that the current position is not whitespace */
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+ line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+ /* we have reached the tag of the package, so create the tag */
+ token = newAdaToken (&line[pos], i, kind, isSpec, parent);
+
+ movePos (i);
+ skipWhiteSpace ();
+
+ /* task and protected types are allowed to have discriminants */
+ if (!eof_reached && line[pos] == '(')
+ {
+ while (!eof_reached && line[pos] != ')')
+ {
+ movePos (1);
+ adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ movePos (1);
+ }
+
+ /* we must parse until we hit the "is" string to reach the end of
+ * this package declaration, or a "renames" keyword */
+ while (token != NULL)
+ {
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_IS))
+ {
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+ {
+ /* if the next word is the keyword "separate", don't create the tag
+ * since it will be defined elsewhere */
+ freeAdaToken (&parent->children, token);
+ token = NULL;
+
+ /* move past the ";" ending this declaration */
+ skipPast (";");
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_NEW))
+ {
+ struct cmpKeywordOrWordDataElt *elt;
+
+ elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
+ .type = ELT_KEYWORD,
+ .u.keyword = ADA_KEYWORD_WITH,
+ }, {
+ .type = ELT_WORD,
+ .u.word = ";",
+ }
+ }, 2);
+
+ if (elt && elt->type == ELT_KEYWORD)
+ adaParse (ADA_DECLARATIONS, token);
+ }
+ else
+ {
+ adaParse (ADA_DECLARATIONS, token);
+ }
+
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
+ {
+ skipPast (";");
+ break;
+ }
+ else if (adaCmp (";"))
+ {
+ token->isSpec = true;
+ break;
+ }
+ else
+ {
+ /* nothing found, move to the next word */
+ skipUntilWhiteSpace ();
+ }
+
+ if (eof_reached)
+ {
+ freeAdaToken (&parent->children, token);
+ token = NULL;
+ }
+ }
+
+ return token;
+}
+
+static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind)
+{
+ int i;
+ adaTokenInfo *token;
+ adaTokenInfo *tmpToken = NULL;
+
+ skipWhiteSpace ();
+
+ /* we are at the start of what should be the tag now... But we have to get
+ * it's length. So loop until we hit whitespace or the beginning of the
+ * parameter list. Init the counter to 1 * since we know that the current
+ * position is not whitespace */
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+ line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+ /* we have reached the tag of the subprogram, so create the tag... Init the
+ * isSpec flag to false and we will adjust it when we see if there is an
+ * "is", "do" or a ";" following the tag */
+ token = newAdaToken (&line[pos], i, kind, false, parent);
+
+ /* move the line position */
+ movePos (i);
+ skipWhiteSpace ();
+
+ /* if we find a '(' grab any parameters */
+ if (!eof_reached && line[pos] == '(' && token != NULL)
+ {
+ while (!eof_reached && line[pos] != ')')
+ {
+ movePos (1);
+ tmpToken = adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ movePos (1);
+
+ /* check to see if anything was received... If this is an entry this may
+ * have a 'discriminant' and not have any parameters in the first
+ * parenthesis pair, so check again if this was the case*/
+ if (kind == ADA_KIND_ENTRY && tmpToken == NULL)
+ {
+ /* skip any existing whitespace and see if there is a second parenthesis
+ * pair */
+ skipWhiteSpace ();
+
+ if (!eof_reached && line[pos] == '(')
+ {
+ while (!eof_reached && line[pos] != ')')
+ {
+ movePos (1);
+ adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ movePos (1);
+ }
+ }
+ }
+
+ /* loop infinitely until we hit a "is", "do" or ";", this will skip over
+ * the returns keyword, returned-type for functions as well as any one of a
+ * myriad of keyword qualifiers */
+ while (!eof_reached && token != NULL)
+ {
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_IS))
+ {
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+ {
+ /* if the next word is the keyword "separate", don't create the tag
+ * since it will be defined elsewhere */
+ freeAdaToken (&parent->children, token);
+ token = NULL;
+
+ /* move past the ";" ending this declaration */
+ skipPast (";");
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_NEW))
+ {
+ /* if this is a "new" something then no need to parse */
+ skipPast (";");
+ }
+ else if (line[pos] == '(')
+ {
+ /* '(' is the starter of an expression function. */
+ skipPast (";");
+ }
+ else
+ {
+ adaParse (ADA_DECLARATIONS, token);
+ }
+
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
+ {
+ skipPast (";");
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_DO))
+ {
+ /* do is the keyword for the beginning of a task entry */
+ adaParse (ADA_CODE, token);
+ break;
+ }
+ else if (adaCmp (";"))
+ {
+ /* this is just a spec then, so set the flag in the token */
+ token->isSpec = true;
+ break;
+ }
+ else
+ {
+ /* nothing found, move to the next word */
+ movePos (1); /* make sure to advance even if we aren't actually on a word */
+ skipPastWord ();
+ }
+ }
+
+ return token;
+}
+
+static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind)
+{
+ int i;
+ adaTokenInfo *token = NULL;
+
+ skipWhiteSpace ();
+
+ /* get the name of the type */
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+ line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+ token = newAdaToken (&line[pos], i, kind, false, parent);
+
+ movePos (i);
+ skipWhiteSpace ();
+
+ if (!eof_reached && line[pos] == '(')
+ {
+ /* in this case there is a discriminant to this type, gather the
+ * variables */
+ while (!eof_reached && line[pos] != ')')
+ {
+ movePos (1);
+ adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ movePos (1);
+ skipWhiteSpace ();
+ }
+
+ /* check to see what is next, if it is not "is" then just skip to the end of
+ * the statement and register this as a 'spec' */
+ if (adaKeywordCmp (ADA_KEYWORD_IS))
+ {
+ skipWhiteSpace ();
+ /* check to see if this may be a record or an enumeration */
+ if (!eof_reached && line[pos] == '(')
+ {
+ movePos (1);
+ adaParseVariables (token, ADA_KIND_ENUM_LITERAL);
+ }
+ else
+ {
+ /* Parsing following form here.
+ *
+ * A. type foo is record ...;
+ * B. type foo is new bar with record ...;
+ * C. type foo is new bar;
+ */
+ if (adaKeywordCmp (ADA_KEYWORD_NEW))
+ {
+ /* B and C */
+ struct cmpKeywordOrWordDataElt *elt;
+ elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
+ .type = ELT_KEYWORD,
+ .u.keyword = ADA_KEYWORD_WITH,
+ }, {
+ .type = ELT_WORD,
+ .u.word = ";",
+ }
+ }, 2);
+ if (elt && elt->type == ELT_WORD)
+ {
+ /* C */
+ return token;
+ }
+
+ /* B */
+ skipWhiteSpace ();
+ }
+ if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+ {
+ /* A and B */
+ /* until we hit "end record" we need to gather type variables */
+ while (!eof_reached)
+ {
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_END))
+ {
+ skipWhiteSpace ();
+ if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+ {
+ break;
+ }
+ skipPast (";");
+ }
+ /* handle variant types */
+ else if (adaKeywordCmp (ADA_KEYWORD_CASE))
+ {
+ skipPastKeyword (ADA_KEYWORD_IS);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+ {
+ skipPast ("=>");
+ }
+ else
+ {
+ adaParseVariables (token, ADA_KIND_RECORD_COMPONENT);
+ skipPast (";");
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ token->isSpec = true;
+ }
+
+ skipPast (";");
+
+ return token;
+}
+
+static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind)
+{
+ /* variables for keeping track of tags */
+ int varEndPos = -1;
+ int tokenStart = -1;
+ adaTokenInfo *token = NULL;
+
+ /* buffer management variables */
+ int i = 0;
+ int bufPos = 0;
+ int bufLen = 0;
+ char *buf = NULL;
+
+ /* file and line position variables */
+ unsigned long int lineNum;
+ int filePosIndex = 0;
+ int filePosSize = 32;
+ MIOPos *filePos = xMalloc (filePosSize, MIOPos);
+
+ /* skip any preliminary whitespace or comments */
+ skipWhiteSpace ();
+ skipComments ();
+
+ /* before we start reading input save the current line number and file
+ * position, so we can reconstruct the correct line & file position for any
+ * tags we create */
+ lineNum = getInputLineNumber ();
+ filePos[filePosIndex] = getInputFilePosition ();
+
+ /* setup local buffer... Since we may have to read a few lines to verify
+ * that this is a proper variable declaration, and still make a token for
+ * each variable, add one to the allocated string to account for a '\0' */
+ bufLen = lineLen - pos;
+ buf = xMalloc (bufLen + 1, char);
+ memcpy ((void *) buf, (void *) &line[pos], bufLen);
+
+ /* don't increase bufLen to include the NULL char so that strlen (buf) and
+ * bufLen match */
+ buf[bufLen] = '\0';
+
+ while (!eof_reached)
+ {
+ /* make sure that we don't count anything in a comment as being valid to
+ * parse */
+ if (isAdaComment (buf, bufPos, bufLen))
+ {
+ /* move bufPos to the end of this 'line' so a new line of input is
+ * read */
+ bufPos = bufLen - 1;
+
+ /* if tokenStart is not -2 then we may be trying to track the type
+ * of this variable declaration, so set tokenStart to -1 so that the
+ * tracking can start over */
+ if (tokenStart != -2)
+ {
+ tokenStart = -1;
+ }
+ }
+ /* we have to keep track of any () pairs that may be in the variable
+ * declarations. And then quit if we hit a ';' the real end ')', or also
+ * a variable initialization... Once we hit := then we have hit the end of
+ * the variable declaration */
+ else if (buf[bufPos] == '(')
+ {
+ i++;
+ }
+ else if (buf[bufPos] == ')')
+ {
+ if (i == 0)
+ {
+ break;
+ }
+ else
+ {
+ i--;
+ }
+ }
+ else if (buf[bufPos] == ';' ||
+ ((bufPos + 1) < bufLen &&
+ (strncasecmp (&buf[bufPos], ":=", strlen (":=")) == 0 ||
+ strncasecmp (&buf[bufPos], "=>", strlen ("=>")) == 0)))
+ {
+ break;
+ }
+ /* if we found the : keep track of where we found it */
+ else if (buf[bufPos] == ':' &&
+ (bufPos + 1 >= bufLen || buf[bufPos + 1] != '='))
+ {
+ varEndPos = bufPos;
+ }
+ /* if we have the position of the ':' find out what the next word is,
+ * because if it "constant" or "exception" then we must tag this slightly
+ * differently, But only check this for normal variables */
+ else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 &&
+ !isspace (buf[bufPos]) && tokenStart == -1)
+ {
+ tokenStart = bufPos;
+ }
+ else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 && tokenStart >= 0 &&
+ ((bufPos + 1) >= bufLen || isspace (buf[bufPos + 1]) ||
+ buf[bufPos + 1] == ';'))
+ {
+ if (cmp (&buf[tokenStart], bufLen - tokenStart,
+ AdaKeywords[ADA_KEYWORD_CONSTANT]) == true)
+ {
+ kind = ADA_KIND_CONSTANT;
+ }
+ else if (cmp (&buf[tokenStart], bufLen - tokenStart,
+ AdaKeywords[ADA_KEYWORD_EXCEPTION]) == true)
+ {
+ kind = ADA_KIND_EXCEPTION;
+ }
+
+ /* set tokenStart to -2 to prevent any more words from being checked */
+ tokenStart = -2;
+ }
+
+ bufPos++;
+
+ /* if we just incremented beyond the length of the current buffer, we need
+ * to read in a new line */
+ if (!eof_reached && bufPos >= bufLen)
+ {
+ readNewLine ();
+
+ /* store the new file position for the start of this line */
+ filePosIndex++;
+ while (filePosIndex >= filePosSize)
+ {
+ filePosSize *= 2;
+ filePos = xRealloc (filePos, filePosSize, MIOPos);
+ }
+ filePos[filePosIndex] = getInputFilePosition ();
+
+ /* increment bufLen and bufPos now so that they jump past the NULL
+ * character in the buffer */
+ bufLen++;
+ bufPos++;
+
+ /* allocate space and store this into our buffer */
+ bufLen += lineLen;
+ buf = xRealloc (buf, bufLen + 1, char);
+ memcpy ((void *) &buf[bufPos], (void *) line, lineLen);
+ buf[bufLen] = '\0';
+ }
+ }
+
+ /* There is a special case if we are gathering enumeration values and we hit
+ * a ')', that is allowed so we need to move varEndPos to where the ')' is */
+ if (kind == ADA_KIND_ENUM_LITERAL && buf[bufPos] == ')' && varEndPos == -1)
+ {
+ varEndPos = bufPos;
+ }
+
+ /* so we found a : or ;... If it is a : go back through the buffer and
+ * create a token for each word skipping over all whitespace and commas
+ * until the : is hit*/
+ if (varEndPos != -1)
+ {
+ /* there should be no whitespace at the beginning, so tokenStart is
+ * initialized to 0 */
+ tokenStart = 0;
+
+ /* before we start set the filePosIndex back to 0 so we can go through the
+ * file position table as the read line number increases */
+ filePosIndex = 0;
+
+ for (i = 0; i < varEndPos; i++)
+ {
+ /* skip comments which are '--' unless we are in a word */
+ if (isAdaComment (buf, i, varEndPos))
+ {
+ /* move i past the '\0' that we put at the end of each line stored in
+ * buf */
+ for ( ; i < varEndPos && buf[i] != '\0'; i++);
+ }
+ else if (tokenStart != -1 && (isspace (buf[i]) || buf[i] == ',' ||
+ buf[i] == '\0'))
+ {
+ /* only store the word if it is not an in/out keyword */
+ if (!cmp (&buf[tokenStart], varEndPos, "in") &&
+ !cmp (&buf[tokenStart], varEndPos, "out"))
+ {
+ token = newAdaToken (&buf[tokenStart], i - tokenStart,
+ kind, false, parent);
+
+ /* now set the proper line and file position counts for this
+ * new token */
+ token->tag.lineNumber = lineNum + filePosIndex;
+ token->tag.filePosition = filePos[filePosIndex];
+ }
+ tokenStart = -1;
+ }
+ else if (tokenStart == -1 && !(isspace (buf[i]) || buf[i] == ',' ||
+ buf[i] == '\0'))
+ {
+ /* only set the tokenStart for non-newline characters */
+ tokenStart = i;
+ }
+
+ /* after we are finished with this line, move the file position */
+ if (buf[i] == '\0')
+ {
+ filePosIndex++;
+ }
+ }
+
+ /* if token start was 'started' then we should store the last token */
+ if (tokenStart != -1)
+ {
+ token = newAdaToken (&buf[tokenStart], i - tokenStart,
+ kind, false, parent);
+
+ /* now set the proper line and file position counts for this
+ * new token */
+ token->tag.lineNumber = lineNum + filePosIndex;
+ token->tag.filePosition = filePos[filePosIndex];
+ }
+ }
+
+ /* now get the pos variable to point to the correct place in line where we
+ * left off in our temp buf, and free our temporary buffer. This is a
+ * little different than most buf position moves. It gets the distance from
+ * the current buf position to the end of the buffer, which is also the
+ * distance from where pos should be wrt the end of the variable
+ * definition */
+ movePos ((lineLen - (bufLen - bufPos)) - pos);
+ eFree ((void *) buf);
+ eFree ((void *) filePos);
+
+ return token;
+}
+
+static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent)
+{
+ int i;
+ adaTokenInfo *token = NULL;
+
+ skipWhiteSpace ();
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]); i++);
+ token = newAdaToken (&line[pos], i, ADA_KIND_AUTOMATIC_VARIABLE, false,
+ parent);
+ movePos (i);
+
+ /* now skip to the end of the loop declaration */
+ skipPastKeyword (ADA_KEYWORD_LOOP);
+
+ return token;
+}
+
+static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent)
+{
+ int i;
+ adaTokenInfo genericParamsRoot;
+ adaTokenInfo *token = NULL;
+
+ initAdaTokenList (&genericParamsRoot.children);
+
+ /* if we hit the end of the file, line will be NULL and our skip and match
+ * functions will hit this jump buffer with eof_reached */
+ while (!eof_reached)
+ {
+ /* find the next place to start */
+ skipWhiteSpace ();
+
+ /* check some universal things to check for first */
+ if (eof_reached)
+ {
+ break;
+ }
+ else if (isAdaComment (line, pos, lineLen))
+ {
+ readNewLine ();
+ continue;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PRAGMA) ||
+ ((mode != ADA_GENERIC) && adaKeywordCmp (ADA_KEYWORD_WITH)) ||
+ adaKeywordCmp (ADA_KEYWORD_USE))
+ {
+ /* set the token to NULL so we accidentally don't pick up something
+ * from earlier
+ * Do not skip lines having 'with' when 'mode == ADA_GENERIC'
+ * this to intercept 'formal subprograms' of a generic declaration.
+ * see: ARM 12.1(22) */
+ skipPast (";");
+ continue;
+ }
+
+ /* check for tags based on our current mode */
+ switch (mode)
+ {
+ case ADA_ROOT:
+ if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+ adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+ {
+ token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+ {
+ token = adaParseBlock (parent, ADA_KIND_TASK);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
+ {
+ /* if we have hit a generic declaration, go to the generic section
+ * and collect the formal parameters */
+ mode = ADA_GENERIC;
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+ {
+ /* skip any possible whitespace */
+ skipWhiteSpace ();
+
+ /* skip over the "(" until we hit the tag */
+ if (!eof_reached && line[pos] == '(')
+ {
+ movePos (1);
+ skipWhiteSpace ();
+
+ /* get length of tag */
+ for (i = 1; (pos + i) < lineLen && line[pos + i] != ')' &&
+ !isspace (line[pos + i]); i++);
+
+ /* the original comment before we introduced reference tags:
+ * -----------------------------------------------------------------
+ * if this is a separate declaration, all it really does is create
+ * a false high level token for everything in this file to belong
+ * to... But we don't know what kind it is, so we declare it as
+ * ADA_KIND_SEPARATE, which will cause it not to be placed in
+ * the tag file, and the item in this file will be printed as
+ * separate:<name> instead of package:<name> or whatever the
+ * parent kind really is (assuming the ctags option will be on
+ * for printing such info to the tag file)
+ * -----------------------------------------------------------------
+ * Now we have reference tags. So we can use ADA_KIND_PACKAGE as kind.
+ */
+ token = newAdaTokenFull (&line[pos], i, ADA_KIND_PACKAGE, ADA_PACKAGE_SUBUNIT,
+ false, parent);
+
+ /* since this is a false top-level token, set parent to be
+ * token */
+ parent = token;
+ token = NULL;
+
+ /* skip past the ')' */
+ skipPast (")");
+ }
+ else
+ {
+ /* move to the end of this statement */
+ skipPast (";");
+ }
+ }
+ else
+ {
+ /* otherwise, nothing was found so just skip until the end of this
+ * unknown statement... It's most likely just a use or with
+ * clause. Also set token to NULL so we don't attempt anything
+ * incorrect */
+ token = NULL;
+ skipPast (";");
+ }
+
+ /* check to see if we succeeded in creating our token */
+ if (token != NULL)
+ {
+ /* if any generic params have been gathered, attach them to
+ * token */
+ appendAdaTokenList (token, &genericParamsRoot.children);
+ }
+
+ break;
+
+ case ADA_GENERIC:
+ /* if we are processing a generic block, make up some temp children
+ * which we will later attach to the root of the real
+ * procedure/package/whatever the formal parameters are for */
+ if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+
+ /* The above 'adaParseBlock' has read the end of a 'generic package declaration',
+ * reset the mode back to the original mode.
+ * see: ARM 12.1(24) */
+ Assert (parent);
+ mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+ adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+ {
+ token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+
+ /* The above 'adaParseBlock' as read the end of a 'generic function/procedure declaration',
+ * reset the mode back to the original mode.
+ * see: ARM 12.1(21/22) */
+ Assert (parent);
+ mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+ {
+ token = adaParseBlock (parent, ADA_KIND_TASK);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
+ {
+ skipWhiteSpace ();
+
+ /* get length of tag */
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+ line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+ appendAdaToken (&genericParamsRoot,
+ newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
+ NULL));
+
+ /* skip to the end of this formal type declaration */
+ skipPast (";");
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WITH))
+ {
+ skipWhiteSpace ();
+ /* skip over the function/procedure keyword, it doesn't matter for
+ * now */
+ skipUntilWhiteSpace ();
+ skipWhiteSpace ();
+
+ /* get length of tag */
+ for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+ line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+ appendAdaToken (&genericParamsRoot,
+ newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
+ NULL));
+
+ /* increment the position */
+ movePos (i);
+
+ /* now gather the parameters to this subprogram */
+ if (!eof_reached && line[pos] == '(')
+ {
+ while (!eof_reached && line[pos] != ')')
+ {
+ movePos (1);
+ adaParseVariables (genericParamsRoot.children.tail,
+ ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ movePos (1);
+ }
+
+ /* skip to the end of this formal type declaration */
+ skipPast (";");
+ }
+ else
+ {
+ /* otherwise, nothing was found so just skip until the end of this
+ * unknown statement... It's most likely just a use or with
+ * clause. Also set token to NULL so we don't attempt anything
+ * incorrect */
+ token = NULL;
+ skipPast (";");
+ }
+
+ /* check to see if we succeeded in creating our token */
+ if (token != NULL)
+ {
+ /* if any generic params have been gathered, attach them to
+ * token. */
+ appendAdaTokenList (token, &genericParamsRoot.children);
+ }
+
+ break;
+
+ case ADA_DECLARATIONS:
+ if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+ adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+ {
+ token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+ {
+ token = adaParseBlock (parent, ADA_KIND_TASK);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+ {
+ token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
+ {
+ /* if we have hit a generic declaration, go to the generic section
+ * and collect the formal parameters */
+ mode = ADA_GENERIC;
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
+ {
+ token = adaParseType (parent, ADA_KIND_TYPE);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_SUBTYPE))
+ {
+ token = adaParseType (parent, ADA_KIND_SUBTYPE);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+ {
+ mode = ADA_CODE;
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+ {
+ /* if we hit a "for" statement it is defining implementation details
+ * for a specific type/variable/subprogram/etc... So we should just
+ * skip it, so skip the tag, then we need to see if there is a
+ * 'record' keyword... If there is we must skip past the
+ * 'end record;' statement. First skip past the tag */
+ skipPastKeyword (ADA_KEYWORD_USE);
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+ {
+ /* now skip to the next "record" keyword, which should be the end
+ * of this use statement */
+ skipPastKeyword (ADA_KEYWORD_RECORD);
+ }
+
+ /* lastly, skip past the end ";" */
+ skipPast (";");
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_END))
+ {
+ /* if we have hit an end then we must see if the next word matches
+ * the parent token's name. If it does we hit the end of whatever
+ * sort of block construct we were processing and we must
+ * return */
+ skipWhiteSpace ();
+ if (adaCmp (parent->name))
+ {
+ skipPast (";");
+
+ /* return the token */
+ freeAdaTokenList (&genericParamsRoot.children);
+ return token;
+ }
+ else
+ {
+ /* set the token to NULL so we accidentally don't pick up something
+ * from earlier */
+ token = NULL;
+ skipPast (";");
+ }
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_ENTRY))
+ {
+ token = adaParseSubprogram (parent, ADA_KIND_ENTRY);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_PRIVATE))
+ {
+ /* if this is a private declaration then we need to set the global
+ * file spec flag and then skip whitespace to get to the next bit of
+ * code to parse. */
+ if (parent != NULL)
+ {
+ parent->isPrivate = true;
+ }
+
+ skipWhiteSpace ();
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_OVERRIDING)
+ || adaKeywordCmp (ADA_KEYWORD_NOT))
+ {
+ /* Do nothing, just ignore these keywords. */
+ ;
+ }
+ else
+ {
+ /* if nothing else matched this is probably a variable, constant
+ * or exception declaration */
+ token = adaParseVariables (parent, ADA_KIND_VARIABLE);
+ skipPast (";");
+ }
+
+ /* check to see if we succeeded in creating our token */
+ if (token != NULL)
+ {
+ /* if this is one of the root-type tokens... Do some extra
+ * processing */
+ if (token->kind == ADA_KIND_PACKAGE ||
+ token->kind == ADA_KIND_SUBPROGRAM ||
+ token->kind == ADA_KIND_TASK ||
+ token->kind == ADA_KIND_PROTECTED)
+ {
+ /* if any generic params have been gathered, attach them to
+ * token */
+ appendAdaTokenList (token, &genericParamsRoot.children);
+ }
+ }
+ break;
+
+ case ADA_CODE:
+ if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
+ {
+ /* if we are starting a declare block here, and not down at the
+ * identifier definition then make an anonymous token to track the
+ * data in this block */
+ token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
+
+ /* save the correct starting line */
+ token->tag.lineNumber = matchLineNum;
+ token->tag.filePosition = matchFilePos;
+
+ adaParse (ADA_DECLARATIONS, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+ {
+ /* if we are starting a code block here, and not down at the
+ * identifier definition then make an anonymous token to track the
+ * data in this block, if this was part of a proper LABEL:
+ * declare/begin/end block then the parent would already be a label
+ * and this begin statement would have been found while in the
+ * ADA_DECLARATIONS parsing section */
+ token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
+
+ /* save the correct starting line */
+ token->tag.lineNumber = matchLineNum;
+ token->tag.filePosition = matchFilePos;
+
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_EXCEPTION))
+ {
+ mode = ADA_EXCEPTIONS;
+ break;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_END))
+ {
+ /* if we have hit an end then we must see if the next word matches
+ * the parent token's name. If it does we hit the end of whatever
+ * sort of block construct we were processing and we must
+ * return */
+ skipWhiteSpace ();
+ if (adaCmp (parent->name))
+ {
+ skipPast (";");
+
+ /* return the token */
+ freeAdaTokenList (&genericParamsRoot.children);
+ return token;
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+ {
+ /* a loop with an identifier has this syntax:
+ * "end loop <ident>;" */
+ skipWhiteSpace ();
+
+ /* now check for the parent loop's name */
+ if (adaCmp (parent->name))
+ {
+ skipPast (";");
+
+ /* return the token */
+ freeAdaTokenList (&genericParamsRoot.children);
+ return token;
+ }
+ }
+ else
+ {
+ /* otherwise, nothing was found so just skip until the end of
+ * this statement */
+ skipPast (";");
+ }
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_ACCEPT))
+ {
+ adaParseSubprogram (parent, ADA_KIND_ENTRY);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+ {
+ /* if this is a for loop, then we may need to pick up the
+ * automatic loop iterator, But... The loop variable is only
+ * available within the loop itself so make an anonymous label
+ * parent for this loop var to be parsed in */
+ token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+ strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+ ADA_KIND_ANONYMOUS, false, parent);
+ adaParseLoopVar (token);
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
+ {
+ token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+ strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+ ADA_KIND_ANONYMOUS, false, parent);
+
+ /* skip past the while loop declaration and parse the loop body */
+ skipPastKeyword (ADA_KEYWORD_LOOP);
+ skipWhiteSpace ();
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+ {
+ token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+ strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+ ADA_KIND_ANONYMOUS, false, parent);
+
+ /* save the correct starting line */
+ token->tag.lineNumber = matchLineNum;
+ token->tag.filePosition = matchFilePos;
+
+ /* parse the loop body */
+ skipWhiteSpace ();
+ adaParse (ADA_CODE, token);
+ }
+ else if (line != NULL &&
+ strncasecmp (&line[pos], "<<", strlen ("<<")) == 0)
+ {
+ movePos (strlen ("<<"));
+
+ /* if the first chars are <<, find the ending >> and if we do that
+ * then store the label tag, start i at strlen of "<<" plus 1
+ * because we don't want to move the real pos until we know for
+ * sure this is a label */
+ for (i = 1; (pos + i) < lineLen &&
+ strncasecmp (&line[pos + i], ">>", strlen (">>")) != 0;
+ i++);
+
+ /* if we didn't increment to the end of the line, a match was
+ * found, if we didn't just fall through */
+ if ((pos + i) < lineLen)
+ {
+ newAdaToken (&line[pos], i, ADA_KIND_LABEL, false, parent);
+ skipPast (">>");
+ }
+ }
+ /* we need to check for a few special case keywords that might cause
+ * the simple ; ending statement checks to fail, first the simple
+ * one word keywords and then the start <stuff> end statements */
+ else if (adaKeywordCmp (ADA_KEYWORD_SELECT) ||
+ adaKeywordCmp (ADA_KEYWORD_OR) ||
+ adaKeywordCmp (ADA_KEYWORD_ELSE))
+ {
+ skipWhiteSpace ();
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_IF) ||
+ adaKeywordCmp (ADA_KEYWORD_ELSIF))
+ {
+ skipPastKeyword (ADA_KEYWORD_THEN);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_CASE))
+ {
+ skipPastKeyword (ADA_KEYWORD_IS);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+ {
+ skipPast ("=>");
+ }
+ else
+ {
+ int i_end;
+ /* set token to NULL so we don't accidentally not find an identifier,
+ * But then fall through to the != NULL check */
+ token = NULL;
+
+ /* there is a possibility that this may be a loop or block
+ * identifier, so check for a <random_word>[ ]?: statement */
+ for (i = 1; (pos + i) < lineLen; i++)
+ if(!(isalnum (line[pos + i]) || line[pos + i] == '_'))
+ break;
+ i_end = i; /* Records the end of identifier. */
+
+ /* Skip whitespaces between the identifier and ':' */
+ for (; (pos + i) < lineLen; i++)
+ if (!isspace((unsigned char)(line[pos + i])))
+ break;
+
+ if ((pos + i) < lineLen
+ && (line[pos + i] == ':')
+ && (
+ ((pos + i + 1) == lineLen)
+ || (line[pos + i + 1] != '=')
+ ))
+ token = newAdaToken (&line[pos], i_end, ADA_KIND_IDENTIFIER, false,
+ parent);
+
+ /* if we created a token, we found an identifier. Now check for a
+ * declare or begin statement to see if we need to start parsing
+ * the following code like a root-style token would */
+ if (token != NULL)
+ {
+ /* if something was found, reset the position variable and try to
+ * find the next item */
+ movePos (i + 1);
+ skipWhiteSpace ();
+
+ if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
+ {
+ adaParse (ADA_DECLARATIONS, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+ {
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+ {
+ /* just grab the automatic loop variable, and then parse the
+ * loop (it may have something to tag which will be a 'child'
+ * of the loop) */
+ adaParseLoopVar (token);
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
+ {
+ /* skip to the loop keyword */
+ skipPastKeyword (ADA_KEYWORD_LOOP);
+ skipWhiteSpace ();
+
+ /* parse the loop (it may have something to tag which will be
+ * a 'child' of the loop) */
+ adaParse (ADA_CODE, token);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+ {
+ skipWhiteSpace ();
+
+ /* parse the loop (it may have something to tag which will be
+ * a 'child' of the loop) */
+ adaParse (ADA_CODE, token);
+ }
+ else
+ {
+ /* otherwise, nothing was found so this is not a valid
+ * identifier, delete it */
+ freeAdaToken (&parent->children, token);
+ token = NULL;
+ }
+ }
+ else
+ {
+ /* since nothing was found, simply skip to the end of this
+ * statement */
+ skipPast (";");
+ }
+ }
+ /* else... No keyword tag fields found, look for others such as
+ * loop and declare identifiers labels or just skip over this
+ * line */
+
+ break;
+
+ case ADA_EXCEPTIONS:
+ if (adaKeywordCmp (ADA_KEYWORD_PRAGMA))
+ {
+ skipPast (";");
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+ {
+ skipWhiteSpace ();
+ token = adaParseVariables (parent, ADA_KIND_AUTOMATIC_VARIABLE);
+ }
+ else if (adaKeywordCmp (ADA_KEYWORD_END))
+ {
+ /* if we have hit an end then we must see if the next word matches
+ * the parent token's name. If it does we hit the end of whatever
+ * sort of block construct we were processing and we must
+ * return */
+ skipWhiteSpace ();
+ if (adaCmp (parent->name))
+ {
+ skipPast (";");
+
+ /* return the token */
+ freeAdaTokenList (&genericParamsRoot.children);
+ return token;
+ }
+ else
+ {
+ /* otherwise, nothing was found so just skip until the end of
+ * this statement */
+ skipPast (";");
+ }
+ }
+ else
+ {
+ /* otherwise, nothing was found so just skip until the end of
+ * this statement */
+ skipPast (";");
+ }
+
+ break;
+
+ default:
+ Assert (0);
+ }
+ }
+
+ freeAdaTokenList (&genericParamsRoot.children);
+ return token;
+}
+
+static void storeAdaTags (adaTokenInfo *token, const char *parentScope)
+{
+ char *currentScope = NULL;
+ adaTokenInfo *tmp = NULL;
+
+ Assert (token);
+
+ /* do a spec transition if necessary */
+ if (token->isSpec == true)
+ {
+ makeSpec (&token->kind);
+
+ if (token->kind != ADA_KIND_UNDEFINED)
+ {
+ token->tag.kindIndex = token->kind;
+ }
+ }
+
+ /* fill in the scope data */
+ if (token->parent != NULL)
+ {
+ if (token->parent->kind != ADA_KIND_UNDEFINED)
+ {
+ token->tag.extensionFields.scopeKindIndex = token->parent->kind;
+ token->tag.extensionFields.scopeName = token->parent->name;
+ }
+ }
+
+ /* one check before we try to make a tag... If this is an anonymous
+ * declare block then it's name is empty. Give it one */
+ if (token->kind == ADA_KIND_ANONYMOUS && token->name == NULL)
+ {
+ token->name = (char *) AdaKeywords[ADA_KEYWORD_DECLARE];
+ token->tag.name = AdaKeywords[ADA_KEYWORD_DECLARE];
+ }
+
+ /* Now 'make' tags that have their options set, But only make anonymous
+ * tags if they have children tags. Also, don't make this tag if the file
+ * scope flag is not set and this tag is a file scope tag. */
+ if ((token->kind > ADA_KIND_UNDEFINED) && (token->kind < ADA_KIND_COUNT) &&
+ (AdaKinds[token->kind].enabled == true) &&
+ (token->name != NULL) &&
+ ((token->kind == ADA_KIND_ANONYMOUS && token->children.head != NULL) ||
+ token->kind != ADA_KIND_ANONYMOUS) &&
+ ((isXtagEnabled (XTAG_FILE_SCOPE) == true) ||
+ ((isXtagEnabled (XTAG_FILE_SCOPE) == false) &&
+ (token->tag.isFileScope == false))))
+ {
+ makeTagEntry (&token->tag);
+
+ /* before making the tag, if the --extra=+q flag is set we should create
+ * an extra entry which is the full parent.tag name. But only do this if
+ * the parentScope flag is not NULL, and this token is not of a limited
+ * scope type such as a record component, enum literal, label, etc. */
+ if ((isXtagEnabled (XTAG_QUALIFIED_TAGS) == true) &&
+ (token->kind != ADA_KIND_RECORD_COMPONENT) &&
+ (token->kind != ADA_KIND_ENUM_LITERAL) &&
+ (token->kind != ADA_KIND_FORMAL) &&
+ (token->kind != ADA_KIND_LABEL) &&
+ (token->kind != ADA_KIND_IDENTIFIER) &&
+ (token->kind != ADA_KIND_AUTOMATIC_VARIABLE) &&
+ (token->kind != ADA_KIND_ANONYMOUS))
+ {
+ if (parentScope != NULL)
+ {
+ /* first create our new scope which is the parent scope + '.' + the
+ * current tag name. */
+ size_t parentScope_len = strlen (parentScope);
+ size_t name_len = strlen (token->name);
+ currentScope = xMalloc (parentScope_len + name_len + 2, char);
+ memcpy (currentScope, parentScope, parentScope_len);
+ currentScope[parentScope_len] = '.';
+ memcpy (¤tScope[parentScope_len + 1], token->name, name_len);
+ currentScope[parentScope_len + 1 + name_len] = '\0';
+
+ token->tag.name = currentScope;
+ markTagExtraBit (&token->tag, XTAG_QUALIFIED_TAGS);
+ makeTagEntry (&token->tag);
+ }
+ else
+ {
+ /* if the parent scope is null then the current token does not have
+ * a parent tag to prepend onto the current scope. Therefore, just
+ * setup the current scope as a copy of the current tag name, but make
+ * no extra entry. */
+ currentScope = token->name;
+ }
+ }
+ }
+
+ /* now make the child tags */
+ tmp = token->children.head;
+ while (tmp != NULL)
+ {
+ storeAdaTags (tmp, currentScope);
+ tmp = tmp->next;
+ }
+
+ /* we have to clear out the declare name here or else it may cause issues
+ * when we try to process it's children, and when we try to free the token
+ * data */
+ if (token->kind == ADA_KIND_ANONYMOUS &&
+ strncasecmp (token->name, AdaKeywords[ADA_KEYWORD_DECLARE],
+ strlen (AdaKeywords[ADA_KEYWORD_DECLARE])) == 0)
+ {
+ token->name = NULL;
+ token->tag.name = NULL;
+ }
+
+ if ((currentScope != NULL) && (currentScope != token->name))
+ {
+ eFree ((void *) currentScope);
+ }
+}
+
+/* main parse function */
+static void findAdaTags (void)
+{
+ adaTokenInfo root;
+ adaTokenInfo *tmp;
+
+ /* init all global data now */
+ eof_reached = false;
+ line = NULL;
+ pos = 0;
+ matchLineNum = 0;
+
+ /* cannot just set matchFilePos to 0 because the fpos_t is not a simple
+ * integer on all systems. */
+ matchFilePos = getInputFilePosition ();
+
+ /* init the root tag */
+ root.kind = ADA_KIND_UNDEFINED;
+ root.isSpec = false;
+ root.name = NULL;
+ root.parent = NULL;
+ root.isPrivate = false;
+ initAdaTokenList (&root.children);
+
+ /* read in the first line */
+ readNewLine ();
+ if (eof_reached)
+ goto out;
+
+ /* tokenize entire file */
+ while (!eof_reached && adaParse (ADA_ROOT, &root) != NULL);
+
+ /* store tags */
+ tmp = root.children.head;
+ while (tmp != NULL)
+ {
+ storeAdaTags (tmp, NULL);
+ tmp = tmp->next;
+ }
+
+ out:
+ /* clean up tokens */
+ freeAdaTokenList (&root.children);
+}
+
+/* parser definition function */
+extern parserDefinition* AdaParser (void)
+{
+ static const char *const extensions[] = { "adb", "ads", "Ada", "ada", NULL };
+ parserDefinition* def = parserNew ("Ada");
+ def->kindTable = AdaKinds;
+ def->kindCount = ADA_KIND_COUNT;
+ def->extensions = extensions;
+ def->parser = findAdaTags;
+ return def;
+}
Modified: meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -589,6 +589,7 @@ ctags = static_library('ctags',
'ctags/main/xtag_p.h',
'ctags/parsers/abaqus.c',
'ctags/parsers/abc.c',
+ 'ctags/parsers/ada.c',
'ctags/parsers/asciidoc.c',
'ctags/parsers/asm.c',
'ctags/parsers/basic.c',
Modified: src/filetypes.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -176,7 +176,7 @@ static void init_builtin_filetypes(void)
FT_INIT( YAML, NONE, "YAML", NULL, FILE, MISC );
FT_INIT( CMAKE, NONE, "CMake", NULL, SOURCE_FILE, SCRIPT );
FT_INIT( NSIS, NSIS, "NSIS", NULL, SOURCE_FILE, SCRIPT );
- FT_INIT( ADA, NONE, "Ada", NULL, SOURCE_FILE, COMPILED );
+ FT_INIT( ADA, ADA, "Ada", NULL, SOURCE_FILE, COMPILED );
FT_INIT( FORTH, NONE, "Forth", NULL, SOURCE_FILE, SCRIPT );
FT_INIT( ASCIIDOC, ASCIIDOC, "Asciidoc", NULL, SOURCE_FILE, MARKUP );
FT_INIT( ABAQUS, ABAQUS, "Abaqus", NULL, SOURCE_FILE, SCRIPT );
Modified: src/tagmanager/tm_parser.c
40 lines changed, 39 insertions(+), 1 deletions(-)
===================================================================
@@ -1031,6 +1031,43 @@ static TMParserMapGroup group_TYPESCRIPT[] = {
{_("Other"), TM_ICON_MEMBER, tm_tag_member_t | tm_tag_enumerator_t},
};
+static TMParserMapEntry map_ADA[] = {
+ {'P', tm_tag_package_t}, // packspec
+ {'p', tm_tag_package_t}, // package
+ {'T', tm_tag_typedef_t}, // typespec
+ {'t', tm_tag_typedef_t}, // type
+ {'U', tm_tag_undef_t}, // subspec
+ {'u', tm_tag_typedef_t}, // subtype
+ {'c', tm_tag_member_t}, // component
+ {'l', tm_tag_enumerator_t}, // literal
+ {'V', tm_tag_undef_t}, // varspec
+ {'v', tm_tag_variable_t}, // variable
+ {'f', tm_tag_undef_t}, // formal
+ {'n', tm_tag_macro_t}, // constant
+ {'x', tm_tag_undef_t}, // exception
+ {'R', tm_tag_prototype_t}, // subprogspec
+ {'r', tm_tag_function_t}, // subprogram
+ {'K', tm_tag_prototype_t}, // taskspec
+ {'k', tm_tag_method_t}, // task
+ {'O', tm_tag_undef_t}, // protectspec
+ {'o', tm_tag_undef_t}, // protected
+ {'E', tm_tag_undef_t}, // entryspec
+ {'e', tm_tag_undef_t}, // entry
+ {'b', tm_tag_undef_t}, // label
+ {'i', tm_tag_undef_t}, // identifier
+ {'a', tm_tag_undef_t}, // autovar
+ {'y', tm_tag_undef_t}, // anon
+};
+static TMParserMapGroup group_ADA[] = {
+ {_("Packages"), TM_ICON_NAMESPACE, tm_tag_package_t},
+ {_("Types"), TM_ICON_STRUCT, tm_tag_typedef_t},
+ {_("Functions"), TM_ICON_METHOD, tm_tag_function_t | tm_tag_prototype_t},
+ {_("Tasks"), TM_ICON_METHOD, tm_tag_method_t},
+ {_("Variables"), TM_ICON_VAR, tm_tag_variable_t},
+ {_("Constants"), TM_ICON_MACRO, tm_tag_macro_t},
+ {_("Other"), TM_ICON_MEMBER, tm_tag_member_t | tm_tag_enumerator_t},
+};
+
typedef struct
{
TMParserMapEntry *entries;
@@ -1075,7 +1112,7 @@ static TMParserMap parser_map[] = {
MAP_ENTRY(HAXE),
MAP_ENTRY(REST),
MAP_ENTRY(HTML),
- MAP_ENTRY(UNUSED1),
+ MAP_ENTRY(ADA),
MAP_ENTRY(CUDA),
MAP_ENTRY(MATLAB),
MAP_ENTRY(VALA),
@@ -1613,6 +1650,7 @@ gboolean tm_parser_has_full_scope(TMParserType lang)
/* These make use of the scope, but don't include nested hierarchy
* (either as a parser limitation or a language semantic) */
+ case TM_PARSER_ADA:
case TM_PARSER_ASCIIDOC:
case TM_PARSER_CLOJURE:
case TM_PARSER_CONF:
Modified: src/tagmanager/tm_parser.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -89,7 +89,7 @@ enum
TM_PARSER_HAXE,
TM_PARSER_REST,
TM_PARSER_HTML,
- TM_PARSER_UNUSED1, /* dummy entry, replace with some new parser */
+ TM_PARSER_ADA,
TM_PARSER_CUDA,
TM_PARSER_MATLAB,
TM_PARSER_VALA,
Modified: src/tagmanager/tm_parsers.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -45,7 +45,7 @@
HaxeParser,\
RstParser, \
HtmlParser, \
- LiterateHaskellParser, /* dummy entry, replace with some new parser */ \
+ AdaParser, \
CUDAParser, \
MatLabParser, \
ValaParser, \
Modified: tests/ctags/Makefile.am
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -19,6 +19,13 @@ test_sources = \
actionscript/method-attributes.as \
actionscript/packages.as \
actionscript/sampler.as \
+ ada-adb.adb \
+ ada-ads.ads \
+ ada-expression-function.adb \
+ ada-expression-function-generic.adb \
+ ada-overriding.ads \
+ ada-package-generic.ads \
+ ada-task.adb \
angle_bracket.cpp \
anonymous_functions.php \
arraylist.js \
Modified: tests/ctags/ada-adb.adb
64 lines changed, 64 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,64 @@
+-- Taken from altivec of GNAT examples (http://www.adacore.com/developers/code-samples/gnat-examples/)
+-- ====================================================================================================
+-- This example shows how to create and manipulate vectors by the mean of high
+-- level views.
+
+with GNAT.Altivec; use GNAT.Altivec;
+with GNAT.Altivec.Conversions; use GNAT.Altivec.Conversions;
+with GNAT.Altivec.Vector_Operations; use GNAT.Altivec.Vector_Operations;
+with GNAT.Altivec.Vector_Types; use GNAT.Altivec.Vector_Types;
+with GNAT.Altivec.Vector_Views; use GNAT.Altivec.Vector_Views;
+
+with GNAT.IO; use GNAT.IO;
+
+procedure Altivec is
+
+ View_A : constant VUI_View := (Values => (1, 2, 3, 4));
+ Vector_A : constant vector_unsigned_int := To_Vector (View_A);
+
+ View_B : constant VUI_View := (Values => (1, 1, 1, 1));
+ Vector_B : constant vector_unsigned_int := To_Vector (View_B);
+
+ Vector_C : vector_unsigned_int;
+ View_C : VUI_View;
+
+begin
+ Vector_C := vec_add (Vector_A, Vector_B);
+ -- C = A + B
+
+ View_C := To_View (Vector_C);
+
+ for I in View_C.Values'Range loop
+ Put_Line (unsigned_int'Image (View_C.Values (I)));
+ end loop;
+
+end Altivec;
+
+procedure Greet is
+ type Days is (Monday, Tuesday, Wednesday,
+ Thursday, Friday,
+ Saturday, Sunday);
+
+ -- Declaration of a subtype
+ subtype Weekend_Days is
+ Days range Saturday .. Sunday;
+ -- ^ Constraint of the subtype
+
+ M : Days := Sunday;
+
+ S : Weekend_Days := M;
+ -- No error here, Days and Weekend_Days
+ -- are of the same type.
+begin
+ for I in Days loop
+ case I is
+ -- Just like a type, a subtype can
+ -- be used as a range
+ when Weekend_Days =>
+ Put_Line ("Week end!");
+ when others =>
+ Put_Line ("Hello on "
+ & Days'Image (I));
+ end case;
+ end loop;
+end Greet;
\ No newline at end of file
Modified: tests/ctags/ada-adb.adb.tags
20 lines changed, 20 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,20 @@
+# format=tagmanager
+Altivec�16�0
+Days�4096�Greet�0
+Friday�4�Days�0
+Greet�16�0
+M�16384�Greet�0
+Monday�4�Days�0
+S�16384�Greet�0
+Saturday�4�Days�0
+Sunday�4�Days�0
+Thursday�4�Days�0
+Tuesday�4�Days�0
+Vector_A�65536�Altivec�0
+Vector_B�65536�Altivec�0
+Vector_C�16384�Altivec�0
+View_A�65536�Altivec�0
+View_B�65536�Altivec�0
+View_C�16384�Altivec�0
+Wednesday�4�Days�0
+Weekend_Days�4096�Greet�0
Modified: tests/ctags/ada-ads.ads
50 lines changed, 50 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,50 @@
+-- Object-oriented generalized stack. This illustrates the use of a
+-- controlled type, which is one that has construction and destructions.
+-- It also shows how to create two related types in the same package so
+-- that they can share private declarations. This sort of thing is
+-- accomplished in Java or C++ using nested classes, or using friend
+-- declarations in C++.
+--
+with Ada.Finalization; use Ada.Finalization;
+
+package GenStack is
+ -- This is the stack type.
+ type Stack is new Controlled with private;
+
+ -- This is the base type for nodes. Client packages must derive their
+ -- nodes from StackData. Since it comes from Controlled, the user can
+ -- override the Initialize, Adjust, and Finalize methods as needed.
+ type StackData is new Controlled with null record;
+
+ -- Initialization operations.
+ procedure Initialize(S: in out Stack);
+ procedure Adjust(S: in out Stack);
+ procedure Finalize(S: in out Stack);
+
+ -- Stack operations.
+ procedure Push(S: in out Stack; D: StackData'class);
+ procedure Pop(S: in out Stack; D: in out StackData'class);
+ procedure Top(S: Stack; Data: in out StackData'class);
+ function Empty(S: Stack) return Boolean;
+
+ private
+ -- Pointer to the node type.
+ type Node;
+ type Node_Ptr is access Node;
+
+ -- Here is the generalized stack itself. We would just make it the
+ -- pointer itself, but it needs to be a record so it can be in a with.
+ type Stack is new Controlled with record
+ Head: Node_Ptr;
+ end record;
+
+ -- Now, we need a pointer to the data part.
+ type Data_Ptr is access StackData'Class;
+
+ -- This is the node type.
+ type Node is record
+ Data: Data_Ptr;
+ Next: Node_Ptr;
+ end record;
+
+end GenStack;
Modified: tests/ctags/ada-ads.ads.tags
17 lines changed, 17 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,17 @@
+# format=tagmanager
+Adjust�1024�GenStack�0
+Data�64�Node�0
+Data_Ptr�4096�GenStack�0
+Empty�1024�GenStack�0
+Finalize�1024�GenStack�0
+GenStack�512�0
+Head�64�Stack�0
+Initialize�1024�GenStack�0
+Next�64�Node�0
+Node�4096�GenStack�0
+Node_Ptr�4096�GenStack�0
+Pop�1024�GenStack�0
+Push�1024�GenStack�0
+Stack�4096�GenStack�0
+StackData�4096�GenStack�0
+Top�1024�GenStack�0
Modified: tests/ctags/ada-expression-function-generic.adb
18 lines changed, 18 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,18 @@
+procedure My_Package is
+
+ generic
+ type Unsigned_Type is range <>;
+ package Generic_Integer_Images is
+ function Digit_To_Character (X : Unsigned_Type) return Character;
+ end Generic_Integer_Images;
+
+ package body Generic_Integer_Images is
+ function Digit_To_Character (X : Unsigned_Type) return Character is
+ (Character'Val (0));
+ end Generic_Integer_Images;
+
+ type Signed_Address is range
+ -2**(Standard'Address_Size - 1) .. 2**(Standard'Address_Size - 1) - 1;
+begin
+ null;
+end My_Package;
Modified: tests/ctags/ada-expression-function-generic.adb.tags
6 lines changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,6 @@
+# format=tagmanager
+Digit_To_Character�16�Generic_Integer_Images�0
+Digit_To_Character�1024�Generic_Integer_Images�0
+Generic_Integer_Images�512�My_Package�0
+My_Package�16�0
+Signed_Address�4096�My_Package�0
Modified: tests/ctags/ada-expression-function.adb
38 lines changed, 38 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,38 @@
+with Ada.Text_IO;
+
+procedure Test is
+
+ My_Type : Boolean := True;
+
+ procedure Tagged_Procedure is
+ function Boolean_As_String return String is
+ (case My_Type is
+ when True => "True Value",
+ when False => "False Value");
+
+ function Another_Boolean_As_String return String is
+ (case My_Type is when True => "; function dummy0 return String is (",
+ when False => "; function dummy1 return String is (");
+ procedure p0 is
+ begin
+ Ada.Text_IO.put ("-0");
+ end p0;
+ function Yet_Boolean_As_String return String is
+ (case My_Type is
+ when True => "1",
+ when False => "0");
+ procedure p1 is
+ begin
+ Ada.Text_IO.put ("-1");
+ end p1;
+ begin
+ null;
+ end Tagged_Procedure;
+
+ procedure Not_Tagged_Procedure is
+ begin
+ null;
+ end Not_Tagged_Procedure;
+begin
+ null;
+end Test;
Modified: tests/ctags/ada-expression-function.adb.tags
10 lines changed, 10 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,10 @@
+# format=tagmanager
+Another_Boolean_As_String�16�Tagged_Procedure�0
+Boolean_As_String�16�Tagged_Procedure�0
+My_Type�16384�Test�0
+Not_Tagged_Procedure�16�Test�0
+Tagged_Procedure�16�Test�0
+Test�16�0
+Yet_Boolean_As_String�16�Tagged_Procedure�0
+p0�16�Tagged_Procedure�0
+p1�16�Tagged_Procedure�0
Modified: tests/ctags/ada-overriding.ads
42 lines changed, 42 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,42 @@
+-- Taken from #2382 submitted by @JulienPivard
+-- @summary
+-- Implémentation par une tâche.
+-- @description
+-- Implémentation par une tache de la classe synchronisé.
+-- @group Version tâche
+--
+-- package Carte_P.Tasche_P
+package Input
+ with
+ Pure => False,
+ Preelaborate => False,
+ Elaborate_Body => True,
+ Spark_Mode => Off
+is
+
+ ---------------------------------------------------------------------------
+ task type Tasche_T is new Carte_T with
+ -- Implémentation par une tâche de l'interface Carte_T.
+
+ -----------------------------------
+ overriding
+ entry Coucou;
+ -- Implémentation par un accept.
+
+ -----------------------------------
+ -- overriding
+ -- entry Inutile;
+ -- Implémentation par un accept.
+ -- @param This
+ -- La carte.
+ end Tasche_T;
+ ---------------------------------------------------------------------------
+
+ overriding
+ procedure Inutile
+ (This : in out Tasche_T);
+ -- Implémentation par une procédure.
+ -- @param This
+ -- La carte.
+
+end Carte_P.Tasche_P;
Modified: tests/ctags/ada-overriding.ads.tags
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,4 @@
+# format=tagmanager
+Input�512�0
+Inutile�1024�Input�0
+Tasche_T�1024�Input�0
Modified: tests/ctags/ada-package-generic.ads
16 lines changed, 16 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,16 @@
+-- Taken from #2925 submitted by @koenmeersman
+package My_Package is
+
+ generic
+ type Num is digits <>;
+ package Conversions is
+ function From_Big_Real (Arg : Integer) return Num;
+ end Conversions;
+
+ type Missing_Tag is record
+ Num : Integer;
+ end record;
+
+ type Primary_Color is (Red, Green, Blue);
+
+end My_Package;
Modified: tests/ctags/ada-package-generic.ads.tags
10 lines changed, 10 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,10 @@
+# format=tagmanager
+Blue�4�Primary_Color�0
+Conversions�512�My_Package�0
+From_Big_Real�1024�Conversions�0
+Green�4�Primary_Color�0
+Missing_Tag�4096�My_Package�0
+My_Package�512�0
+Num�64�Missing_Tag�0
+Primary_Color�4096�My_Package�0
+Red�4�Primary_Color�0
Modified: tests/ctags/ada-task.adb
19 lines changed, 19 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,19 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+procedure Show_Simple_Tasks is
+ task T;
+ task T2;
+
+ task body T is
+ begin
+ Put_Line ("In task T");
+ end T;
+
+ task body T2 is
+ begin
+ Put_Line ("In task T2");
+ end T2;
+
+begin
+ Put_Line ("In main");
+end Show_Simple_Tasks;
Modified: tests/ctags/ada-task.adb.tags
6 lines changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,6 @@
+# format=tagmanager
+Show_Simple_Tasks�16�0
+T�128�Show_Simple_Tasks�0
+T�1024�Show_Simple_Tasks�0
+T2�128�Show_Simple_Tasks�0
+T2�1024�Show_Simple_Tasks�0
Modified: tests/meson.build
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -18,6 +18,13 @@ ctags_tests = files([
'ctags/actionscript/method-attributes.as.tags',
'ctags/actionscript/packages.as.tags',
'ctags/actionscript/sampler.as.tags',
+ 'ctags/ada-adb.adb.tags',
+ 'ctags/ada-ads.ads.tags',
+ 'ctags/ada-expression-function.adb.tags',
+ 'ctags/ada-expression-function-generic.adb.tags',
+ 'ctags/ada-overriding.ads.tags',
+ 'ctags/ada-package-generic.ads.tags',
+ 'ctags/ada-task.adb.tags',
'ctags/angle_bracket.cpp.tags',
'ctags/anonymous_functions.php.tags',
'ctags/arraylist.js.tags',
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Jiří Techet <techet(a)gmail.com>
Date: Mon, 11 Apr 2022 15:05:35 UTC
Commit: 13a58c2b2e58d4a3157e93a7f2bd0d006b884cdf
https://github.com/geany/geany/commit/13a58c2b2e58d4a3157e93a7f2bd0d006b884…
Log Message:
-----------
Fix anonymous tag renaming for Fortran structures
See comment in the added code.
Modified Paths:
--------------
src/tagmanager/tm_ctags.c
Modified: src/tagmanager/tm_ctags.c
16 lines changed, 16 insertions(+), 0 deletions(-)
===================================================================
@@ -257,6 +257,7 @@ static void rename_anon_tags(TMSourceFile *source_file)
gchar *orig_name, *new_name = NULL;
guint j;
guint new_name_len, orig_name_len;
+ gboolean inside_nesting = FALSE;
guint scope_len = tag->scope ? strlen(tag->scope) : 0;
gchar kind = tag->kind_letter;
@@ -332,6 +333,21 @@ static void rename_anon_tags(TMSourceFile *source_file)
guint nested_scope_len = nested_tag->scope ? strlen(nested_tag->scope) : 0;
gchar *pos;
+ /* In Fortran, we can create variables of anonymous structures:
+ * structure var1, var2
+ * integer a
+ * end structure
+ * and the parser first generates tags for var1 and var2 which
+ * are on the same scope as the structure itself. So first
+ * we need to skip past the tags on the same scope and only
+ * afterwards we get the nested tags.
+ * */
+ if ((source_file->lang == TM_PARSER_F77 || source_file->lang == TM_PARSER_FORTRAN) &&
+ !inside_nesting && nested_scope_len == scope_len)
+ continue;
+
+ inside_nesting = TRUE;
+
/* Terminate if outside of tag scope, see above */
if (nested_scope_len <= scope_len)
break;
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Jiří Techet <techet(a)gmail.com>
Date: Sat, 19 Mar 2022 16:50:30 UTC
Commit: 1bc7d8ff4eefc859f9bf2c2bf81d0952f8663c40
https://github.com/geany/geany/commit/1bc7d8ff4eefc859f9bf2c2bf81d0952f8663…
Log Message:
-----------
Store project files in base directory by default
Project files are, by default, stored inside the configured "projects"
directory. The problem is that for new users of Geany, the default
~/projects most probably points to a non-existent directory and then,
the project creation experience is not quite optimal.
In addition, this is what most editors do and most Geany newcomers
will expect this behavior.
Modified Paths:
--------------
src/keyfile.c
Modified: src/keyfile.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -801,7 +801,7 @@ static void load_dialog_prefs(GKeyFile *config)
prefs.confirm_exit = utils_get_setting_boolean(config, PACKAGE, "pref_main_confirm_exit", FALSE);
prefs.suppress_status_messages = utils_get_setting_boolean(config, PACKAGE, "pref_main_suppress_status_messages", FALSE);
prefs.load_session = utils_get_setting_boolean(config, PACKAGE, "pref_main_load_session", TRUE);
- project_prefs.project_file_in_basedir = utils_get_setting_boolean(config, PACKAGE, "pref_main_project_file_in_basedir", FALSE);
+ project_prefs.project_file_in_basedir = utils_get_setting_boolean(config, PACKAGE, "pref_main_project_file_in_basedir", TRUE);
prefs.save_winpos = utils_get_setting_boolean(config, PACKAGE, "pref_main_save_winpos", TRUE);
prefs.save_wingeom = utils_get_setting_boolean(config, PACKAGE, "pref_main_save_wingeom", prefs.save_winpos);
prefs.beep_on_errors = utils_get_setting_boolean(config, PACKAGE, "beep_on_errors", TRUE);
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Jiří Techet <techet(a)gmail.com>
Date: Sat, 19 Mar 2022 16:50:30 UTC
Commit: f8768399d32b817ecaccd2f28f43e8e0d55a06e6
https://github.com/geany/geany/commit/f8768399d32b817ecaccd2f28f43e8e0d55a0…
Log Message:
-----------
Make the New Project dialog a little wider
With the current width, the created project file name gets hidden even
for not-so-long paths. Consider for instance the following path:
/home/my_name/projects/project/project.geany
It consists of 43 characters where "/home/my_name/projects/" is a typical
prefix of someone with not terribly long user name and also
"project/project.geany" is fairly normal project name and directory name
length. Even though the patch says 40, this 43-character string still fits
in on my machine (50 makes the window a little too wide).
Modified Paths:
--------------
src/project.c
Modified: src/project.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -195,7 +195,7 @@ void project_new(gboolean from_folder)
e.file_name = gtk_entry_new();
gtk_entry_set_activates_default(GTK_ENTRY(e.file_name), TRUE);
ui_entry_add_clear_icon(GTK_ENTRY(e.file_name));
- gtk_entry_set_width_chars(GTK_ENTRY(e.file_name), 30);
+ gtk_entry_set_width_chars(GTK_ENTRY(e.file_name), 40);
tooltip = g_strdup_printf(
_("Path of the file representing the project and storing its settings. "
"It should normally have the \"%s\" extension."), "."GEANY_PROJECT_EXT);
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Jiří Techet <techet(a)gmail.com>
Committer: Jiří Techet <techet(a)gmail.com>
Date: Sat, 19 Mar 2022 16:50:30 UTC
Commit: 8abf31d3c6f559df18505fd1c69ac771b23cd94c
https://github.com/geany/geany/commit/8abf31d3c6f559df18505fd1c69ac771b23cd…
Log Message:
-----------
Improve project directory determination
When using "New from Folder", open the directory selection dialog at
current document's directory to simplify new project creation for
opened files. Fall back to "project files" directory if no file opened
and if this directory isn't set in preferences, fall back to user's
home directory.
In addition take into account that the "project files" directory may not
be set when filling in entries in the "New Project" dialog and fall back
to currently opened document's directory or to home directory when no
document is opened.
Modified Paths:
--------------
src/project.c
Modified: src/project.c
30 lines changed, 27 insertions(+), 3 deletions(-)
===================================================================
@@ -157,7 +157,19 @@ void project_new(gboolean from_folder)
if (from_folder)
{
- base_path = ui_get_project_directory(local_prefs.project_file_path);
+ GeanyDocument *doc = document_get_current();
+ gchar *start_path;
+
+ if (doc && doc->file_name)
+ start_path = g_path_get_dirname(doc->file_name);
+ else if (!EMPTY(local_prefs.project_file_path))
+ start_path = g_strdup(local_prefs.project_file_path);
+ else
+ start_path = utils_get_utf8_from_locale(g_get_home_dir());
+
+ base_path = ui_get_project_directory(start_path);
+ g_free(start_path);
+
if (!base_path)
return;
}
@@ -973,12 +985,23 @@ static void update_new_project_dlg(GtkEditable *editable, PropertyDialogElements
{
gchar *base_path;
gchar *file_name;
- gchar *name;
- const gchar *project_dir = local_prefs.project_file_path;
+ gchar *project_dir = NULL;
if (e->entries_modified)
return;
+ if (!EMPTY(local_prefs.project_file_path))
+ project_dir = g_strdup(local_prefs.project_file_path);
+ else
+ {
+ GeanyDocument *doc = document_get_current();
+
+ if (doc && doc->file_name)
+ project_dir = g_path_get_dirname(doc->file_name);
+ else
+ project_dir = utils_get_utf8_from_locale(g_get_home_dir());
+ }
+
if (!EMPTY(base_p))
{
gchar *name = g_path_get_basename(base_p);
@@ -1022,6 +1045,7 @@ static void update_new_project_dlg(GtkEditable *editable, PropertyDialogElements
g_free(base_path);
g_free(file_name);
+ g_free(project_dir);
}
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).