diff options
-rw-r--r-- | .gitlab-ci.yml | 1 | ||||
-rw-r--r-- | data/.gitignore | 1 | ||||
-rwxr-xr-x | data/lineup-parameters | 210 | ||||
-rw-r--r-- | data/lineup-parameters.c | 483 | ||||
-rw-r--r-- | data/meson.build | 10 | ||||
-rwxr-xr-x | data/run-uncrustify.sh | 3 |
6 files changed, 211 insertions, 497 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d2fa263c..ef12ec992 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,6 @@ style check: - 'uncrustify.diff' expire_in: 14 days script: - - gcc data/lineup-parameters.c `pkg-config --cflags --libs gio-2.0 gio-unix-2.0` -o data/lineup-parameters - LANG=C.utf8 data/run-uncrustify.sh - git diff --exit-code | tee uncrustify.diff except: diff --git a/data/.gitignore b/data/.gitignore index 89808d77c..511518769 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,4 +1,3 @@ -/lineup-parameters /nautilus-autorun-software.desktop /org.freedesktop.FileManager1.service /org.gnome.Nautilus.appdata.xml diff --git a/data/lineup-parameters b/data/lineup-parameters new file mode 100755 index 000000000..aae22de0e --- /dev/null +++ b/data/lineup-parameters @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# +# Copyright © 2019 Michael Catanzaro <mcatanzaro@gnome.org> +# +# 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 3 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, see <http://www.gnu.org/licenses/>. + +# Based on original C lineup-parameters by Sébastien Wilmet <swilmet@gnome.org> +# Rewritten in Python to allow simple execution from source directory. +# +# Usage: lineup-parameters [file] +# If the file is not given, stdin is read. +# The result is printed to stdout. +# +# The restrictions: +# - The function name must be at column 0, followed by a space and an opening +# parenthesis; +# - One parameter per line; +# - A parameter must follow certain rules (see the regex in the code), but it +# doesn't accept all possibilities of the C language. +# - The opening curly brace ("{") of the function must also be at column 0. +# +# If one restriction is missing, the function declaration is not modified. +# +# Example: +# +# gboolean +# frobnitz (Frobnitz *frobnitz, +# gint magic_number, +# GError **error) +# { +# ... +# } +# +# Becomes: +# +# gboolean +# frobnitz (Frobnitz *frobnitz, +# gint magic_number, +# GError **error) +# { +# ... +# } +# +# TODO: support "..." vararg parameter + +import argparse +import re +import sys + +from typing import NamedTuple + +# https://regexr.com/ is your friend. +functionNameRegex = re.compile(r'^(\w+) ?\(') +parameterRegex = re.compile( + r'^\s*(?P<type>(const\s+)?\w+)' + r'\s+(?P<stars>\**)' + r'\s*(?P<name>\w+)' + r'\s*(?P<end>,|\))' + r'\s*$') +openingCurlyBraceRegex = re.compile(r'^{\s*$') + + +def matchFunctionName(line): + match = functionNameRegex.match(line) + if match: + functionName = match.group(1) + firstParamPosition = match.end(0) + return (functionName, firstParamPosition) + return (None, 0) + + +class ParameterInfo(NamedTuple): + paramType: str + name: str + numStars: int + isLastParameter: bool + + +def matchParameter(line): + _, firstParamPosition = matchFunctionName(line) + match = parameterRegex.match(line[firstParamPosition:]) + if match is None: + return None + paramType = match.group('type') + assert(paramType is not None) + name = match.group('name') + assert(name is not None) + stars = match.group('stars') + numStars = len(stars) if stars is not None else 0 + end = match.group('end') + isLastParameter = True if end is not None and end == ')' else False + return ParameterInfo(paramType, name, numStars, isLastParameter) + + +def matchOpeningCurlyBrace(line): + return True if openingCurlyBraceRegex.match(line) is not None else False + + +# Length returned is number of lines the declaration takes up +def getFunctionDeclarationLength(remainingLines): + for i in range(len(remainingLines)): + currentLine = remainingLines[i] + parameterInfo = matchParameter(currentLine) + if parameterInfo is None: + return 0 + if parameterInfo.isLastParameter: + if i + 1 == len(remainingLines): + return 0 + nextLine = remainingLines[i + 1] + if not matchOpeningCurlyBrace(nextLine): + return 0 + return i + 1 + return 0 + + +def getParameterInfos(remainingLines, length): + parameterInfos = [] + for i in range(length): + parameterInfos.append(matchParameter(remainingLines[i])) + return parameterInfos + + +def computeSpacing(parameterInfos): + maxTypeLength = 0 + maxStarsLength = 0 + for parameterInfo in parameterInfos: + maxTypeLength = max(maxTypeLength, len(parameterInfo.paramType)) + maxStarsLength = max(maxStarsLength, parameterInfo.numStars) + return (maxTypeLength, maxStarsLength) + + +def printParameter(parameterInfo, maxTypeLength, maxStarsLength, outfile): + outfile.write(f'{parameterInfo.paramType:<{maxTypeLength + 1}}') + paramNamePaddedWithStars = f'{parameterInfo.name:*>{parameterInfo.numStars + len(parameterInfo.name)}}' + outfile.write(f'{paramNamePaddedWithStars:>{maxStarsLength + len(parameterInfo.name)}}') + + +def printFunctionDeclaration(remainingLines, length, useTabs, outfile): + functionName, _ = matchFunctionName(remainingLines[0]) + assert(functionName is not None) + outfile.write(f'{functionName} (') + numSpacesToParenthesis = len(functionName) + 2 + whitespace = '' + if useTabs: + tabs = ''.ljust(numSpacesToParenthesis // 8, '\t') + spaces = ''.ljust(numSpacesToParenthesis % 8) + whitespace = tabs + spaces + else: + whitespace = ''.ljust(numSpacesToParenthesis) + parameterInfos = getParameterInfos(remainingLines, length) + maxTypeLength, maxStarsLength = computeSpacing(parameterInfos) + numParameters = len(parameterInfos) + for i in range(numParameters): + parameterInfo = parameterInfos[i] + if i != 0: + outfile.write(whitespace) + printParameter(parameterInfo, maxTypeLength, maxStarsLength, outfile) + if i + 1 != numParameters: + outfile.write(',\n') + outfile.write(')\n') + + +def parseContents(infile, useTabs, outfile): + lines = infile.readlines() + i = 0 + while i < len(lines): + line = lines[i] + functionName, _ = matchFunctionName(line) + if functionName is None: + outfile.write(line) + i += 1 + continue + remainingLines = lines[i:] + length = getFunctionDeclarationLength(remainingLines) + if length == 0: + outfile.write(line) + i += 1 + continue + printFunctionDeclaration(remainingLines, length, useTabs, outfile) + i += length + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='Line up parameters of C functions') + parser.add_argument('infile', nargs='?', + type=argparse.FileType('r'), + default=sys.stdin, + help='input C source file') + parser.add_argument('-o', metavar='outfile', nargs='?', + type=argparse.FileType('w'), + default=sys.stdout, + help='where to output modified file') + parser.add_argument('--tabs', action='store_true', + help='whether use tab characters in the output') + args = parser.parse_args() + parseContents(args.infile, args.tabs, args.o) + args.infile.close() + args.o.close() diff --git a/data/lineup-parameters.c b/data/lineup-parameters.c deleted file mode 100644 index 51a01e119..000000000 --- a/data/lineup-parameters.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * This file is part of gnome-c-utils. - * - * Copyright © 2013 Sébastien Wilmet <swilmet@gnome.org> - * - * gnome-c-utils 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 3 of the License, or - * (at your option) any later version. - * - * gnome-c-utils 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 gnome-c-utils. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Line up parameters of function declarations. - * - * Usage: lineup-parameters [file] - * If the file is not given, stdin is read. - * The result is printed to stdout. - * - * The restrictions: - * - The function name must be at column 0, followed by a space and an opening - * parenthesis; - * - One parameter per line; - * - A paramater must follow certain rules (see the regex in the code), but it - * doesn't accept all possibilities of the C language. - * - The opening curly brace ("{") of the function must also be at column 0. - * - * If one restriction is missing, the function declaration is not modified. - * - * Example: - * - * gboolean - * frobnitz (Frobnitz *frobnitz, - * gint magic_number, - * GError **error) - * { - * ... - * } - * - * Becomes: - * - * gboolean - * frobnitz (Frobnitz *frobnitz, - * gint magic_number, - * GError **error) - * { - * ... - * } - */ - -/* - * Use with Vim: - * - * Although this script can be used in Vim (or other text editors), a Vim plugin - * exists: - * http://damien.lespiau.name/blog/2009/12/07/aligning-c-function-parameters-with-vim/ - * - * You can use a selection: - * - place the cursor at the function's name; - * - press V to start the line selection; - * - press ]] to go to the "{"; - * - type ":" followed by "!lineup-parameters". - * - * Note: the "{" is required in the selection, to detect that we are in a - * function declaration. - * - * You can easily map these steps with a keybinding (F8 in the example below). - * Note that I'm not a Vim expert, so there is maybe a better way to configure - * this stuff. - * - * function! LineupParameters() - * let l:winview = winsaveview() - * execute "normal {V]]:!lineup-parameters\<CR>" - * call winrestview(l:winview) - * endfunction - * - * autocmd Filetype c map <F8> :call LineupParameters()<CR> - */ - -/* TODO support "..." vararg parameter. */ - -#include <gio/gio.h> -#include <gio/gunixinputstream.h> -#include <stdlib.h> -#include <string.h> -#include <locale.h> -#include <unistd.h> - -#define USE_TABS FALSE - -typedef struct -{ - gchar *type; - guint nb_stars; - gchar *name; -} ParameterInfo; - -static void -parameter_info_free (ParameterInfo *param_info) -{ - g_free (param_info->type); - g_free (param_info->name); - g_slice_free (ParameterInfo, param_info); -} - -static gboolean -match_function_name (const gchar *line, - gchar **function_name, - gint *first_param_pos) -{ - static GRegex *regex = NULL; - GMatchInfo *match_info; - gint end_pos; - gboolean match = FALSE; - - if (G_UNLIKELY (regex == NULL)) - regex = g_regex_new ("^(\\w+) ?\\(", G_REGEX_OPTIMIZE, 0, NULL); - - g_regex_match (regex, line, 0, &match_info); - - if (g_match_info_matches (match_info) && - g_match_info_fetch_pos (match_info, 1, NULL, &end_pos) && - g_match_info_fetch_pos (match_info, 0, NULL, first_param_pos)) - { - match = TRUE; - - if (function_name != NULL) - *function_name = g_strndup (line, end_pos); - } - - g_match_info_free (match_info); - return match; -} - -static gboolean -match_parameter (gchar *line, - ParameterInfo **info, - gboolean *is_last_parameter) -{ - static GRegex *regex = NULL; - GMatchInfo *match_info; - gint start_pos = 0; - - if (G_UNLIKELY (regex == NULL)) - regex = g_regex_new ("^\\s*(?<type>(const\\s+)?\\w+)\\s+(?<stars>\\**)\\s*(?<name>\\w+)\\s*(?<end>,|\\))\\s*$", - G_REGEX_OPTIMIZE, - 0, - NULL); - - if (is_last_parameter != NULL) - *is_last_parameter = FALSE; - - match_function_name (line, NULL, &start_pos); - - g_regex_match (regex, line + start_pos, 0, &match_info); - - if (!g_match_info_matches (match_info)) - { - g_match_info_free (match_info); - return FALSE; - } - - if (info != NULL) - { - gchar *stars; - - *info = g_slice_new0 (ParameterInfo); - - (*info)->type = g_match_info_fetch_named (match_info, "type"); - (*info)->name = g_match_info_fetch_named (match_info, "name"); - g_assert ((*info)->type != NULL); - g_assert ((*info)->name != NULL); - - stars = g_match_info_fetch_named (match_info, "stars"); - (*info)->nb_stars = strlen (stars); - g_free (stars); - } - - if (is_last_parameter != NULL) - { - gchar *end = g_match_info_fetch_named (match_info, "end"); - *is_last_parameter = g_str_equal (end, ")"); - g_free (end); - } - - g_match_info_free (match_info); - return TRUE; -} - -static gboolean -match_opening_curly_brace (const gchar *line) -{ - static GRegex *regex = NULL; - - if (G_UNLIKELY (regex == NULL)) - regex = g_regex_new ("^{\\s*$", G_REGEX_OPTIMIZE, 0, NULL); - - return g_regex_match (regex, line, 0, NULL); -} - -/* Returns the number of lines that take the function declaration. - * Returns 0 if not a function declaration. */ -static guint -get_function_declaration_length (gchar **lines) -{ - guint nb_lines = 1; - gchar **cur_line = lines; - - while (*cur_line != NULL) - { - gboolean match_param; - gboolean is_last_param; - - match_param = match_parameter (*cur_line, NULL, &is_last_param); - - if (is_last_param) - { - gchar *next_line = *(cur_line + 1); - - if (next_line == NULL || - !match_opening_curly_brace (next_line)) - return 0; - - return nb_lines; - } - - if (!match_param) - return 0; - - nb_lines++; - cur_line++; - } - /* should not be reachable - but silences a compiler warning */ - return 0; -} - -static GSList * -get_list_parameter_infos (gchar **lines, - guint length) -{ - GSList *list = NULL; - gint i; - - for (i = length - 1; i >= 0; i--) - { - ParameterInfo *info = NULL; - - match_parameter (lines[i], &info, NULL); - g_assert (info != NULL); - - list = g_slist_prepend (list, info); - } - - return list; -} - -static void -compute_spacing (GSList *parameter_infos, - guint *max_type_length, - guint *max_stars_length) -{ - GSList *l; - *max_type_length = 0; - *max_stars_length = 0; - - for (l = parameter_infos; l != NULL; l = l->next) - { - ParameterInfo *info = l->data; - guint type_length = strlen (info->type); - - if (type_length > *max_type_length) - *max_type_length = type_length; - - if (info->nb_stars > *max_stars_length) - *max_stars_length = info->nb_stars; - } -} - -static void -print_parameter (ParameterInfo *info, - guint max_type_length, - guint max_stars_length) -{ - gint type_length; - gint nb_spaces; - gchar *spaces; - gchar *stars; - - g_print ("%s", info->type); - - type_length = strlen (info->type); - nb_spaces = max_type_length - type_length; - g_assert (nb_spaces >= 0); - - spaces = g_strnfill (nb_spaces, ' '); - g_print ("%s ", spaces); - g_free (spaces); - - nb_spaces = max_stars_length - info->nb_stars; - g_assert (nb_spaces >= 0); - spaces = g_strnfill (nb_spaces, ' '); - g_print ("%s", spaces); - g_free (spaces); - - stars = g_strnfill (info->nb_stars, '*'); - g_print ("%s", stars); - g_free (stars); - - g_print ("%s", info->name); -} - -static void -print_function_declaration (gchar **lines, - guint length) -{ - gchar **cur_line = lines; - gchar *function_name; - gint nb_spaces_to_parenthesis; - GSList *parameter_infos; - GSList *l; - guint max_type_length; - guint max_stars_length; - gchar *spaces; - - if (!match_function_name (*cur_line, &function_name, NULL)) - g_error ("The line doesn't match a function name."); - - g_print ("%s (", function_name); - - nb_spaces_to_parenthesis = strlen (function_name) + 2; - - if (USE_TABS) - { - gchar *tabs = g_strnfill (nb_spaces_to_parenthesis / 8, '\t'); - gchar *spaces_after_tabs = g_strnfill (nb_spaces_to_parenthesis % 8, ' '); - - spaces = g_strdup_printf ("%s%s", tabs, spaces_after_tabs); - - g_free (tabs); - g_free (spaces_after_tabs); - } - else - { - spaces = g_strnfill (nb_spaces_to_parenthesis, ' '); - } - - parameter_infos = get_list_parameter_infos (lines, length); - compute_spacing (parameter_infos, &max_type_length, &max_stars_length); - - for (l = parameter_infos; l != NULL; l = l->next) - { - ParameterInfo *info = l->data; - - if (l != parameter_infos) - g_print ("%s", spaces); - - print_parameter (info, max_type_length, max_stars_length); - - if (l->next != NULL) - g_print (",\n"); - } - - g_print (")\n"); - - g_free (function_name); - g_free (spaces); - g_slist_free_full (parameter_infos, (GDestroyNotify)parameter_info_free); -} - -static void -parse_contents (gchar **lines) -{ - gchar **cur_line = lines; - - /* Skip the empty last line, to avoid adding an extra \n. */ - for (cur_line = lines; cur_line[0] != NULL && cur_line[1] != NULL; cur_line++) - { - guint length; - - if (!match_function_name (*cur_line, NULL, NULL)) - { - g_print ("%s\n", *cur_line); - continue; - } - - length = get_function_declaration_length (cur_line); - - if (length == 0) - { - g_print ("%s\n", *cur_line); - continue; - } - - print_function_declaration (cur_line, length); - - cur_line += length - 1; - } -} - -static gchar * -get_file_contents (gchar *arg) -{ - GFile *file; - gchar *path; - gchar *contents; - GError *error = NULL; - - file = g_file_new_for_commandline_arg (arg); - path = g_file_get_path (file); - g_file_get_contents (path, &contents, NULL, &error); - - if (error != NULL) - g_error ("Impossible to get file contents: %s", error->message); - - g_object_unref (file); - g_free (path); - return contents; -} - -static gchar * -get_stdin_contents (void) -{ - GInputStream *stream; - GString *string; - GError *error = NULL; - - stream = g_unix_input_stream_new (STDIN_FILENO, FALSE); - string = g_string_new (""); - - while (TRUE) - { - gchar buffer[4097] = { '\0' }; - gssize nb_bytes_read = g_input_stream_read (stream, buffer, 4096, NULL, &error); - - if (nb_bytes_read == 0) - break; - - if (error != NULL) - g_error ("Impossible to read stdin: %s", error->message); - - g_string_append (string, buffer); - } - - g_input_stream_close (stream, NULL, NULL); - g_object_unref (stream); - - return g_string_free (string, FALSE); -} - -gint -main (gint argc, - gchar *argv[]) -{ - gchar *contents; - gchar **contents_lines; - - setlocale (LC_ALL, ""); - - if (argc > 2) - { - g_printerr ("Usage: %s [file]\n", argv[0]); - return EXIT_FAILURE; - } - - if (argc == 2) - contents = get_file_contents (argv[1]); - else - contents = get_stdin_contents (); - - contents_lines = g_strsplit (contents, "\n", 0); - g_free (contents); - - parse_contents (contents_lines); - - return EXIT_SUCCESS; -} diff --git a/data/meson.build b/data/meson.build index cc1561d89..f27426a5c 100644 --- a/data/meson.build +++ b/data/meson.build @@ -15,16 +15,6 @@ install_data( po_dir = join_paths(meson.source_root(), 'po') -line_up_parameters = executable( - 'lineup-parameters', - 'lineup-parameters.c', - build_by_default: false, - dependencies: [ - gio, - gio_unix, - ] -) - desktop_conf = configuration_data() desktop_conf.set('icon', application_id) diff --git a/data/run-uncrustify.sh b/data/run-uncrustify.sh index 78ac8d6cd..a50c0d3dd 100755 --- a/data/run-uncrustify.sh +++ b/data/run-uncrustify.sh @@ -10,8 +10,7 @@ fi if [ ! -x "$DATA/lineup-parameters" ]; then - echo "Script lineup-parameters does not exists here in (source directory)/data, probably because Nautilus was built in a different directory than the source directory. -Copy the program in the (build directory)/data/lineup-parameters here in (source directory)/data and run again run-uncrustify.sh." + echo "Script lineup-parameters does not exists." exit 1 fi |