Skip to content

Commit

Permalink
Add helper script for generating ctags files in Python
Browse files Browse the repository at this point in the history
  • Loading branch information
eht16 committed Jun 4, 2023
1 parent d6ce258 commit a6c04ed
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 26 deletions.
31 changes: 5 additions & 26 deletions scripts/create_py_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
# docker run --rm -it --user $(id -u):$(id -g) -v $(pwd):/data --workdir /data python:3.11-alpine python scripts/create_py_tags.py
#

import datetime
import importlib.util
import inspect
import os
import platform
import re
import sys
import sysconfig
import warnings
from pathlib import Path

from create_tags_helper import format_tag, write_ctags_file

# treat all DeprecationWarnings as errors so we can catch them to ignore the corresponding modules
warnings.filterwarnings('error', category=DeprecationWarning)

Expand All @@ -56,15 +56,6 @@
TAG_REGEXP = re.compile(r'^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*(\(.*\))[:]')
OBJECT_MEMORY_ADDRESS_REGEXP = re.compile(r'<(.+?) at 0x[0-9a-f]+(?:.+)>', flags=re.IGNORECASE)

CTAGS_FILE_HEADER = f'''!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROGRAM_NAME scripts/create_py_tags.py Automatically generated file - do not edit (created on {datetime.datetime.now().ctime()} with Python {platform.python_version()})
'''

# pylint: disable=no-else-return,no-self-use


Expand All @@ -89,13 +80,7 @@ def _add_tag(self, object_name, object_, kind, module_path=None, parent=''):
tag_key = (module_path, parent, object_name)
if tag_key not in self.tags:
signature = self._create_signature(object_) if object_ is not None else None
self.tags[tag_key] = self._format_tag(object_name, kind, signature, parent)

def _format_tag(self, tagname, kind, signature, parent):
signature_field = f'\tsignature:{signature}' if signature else ''
parent_field = f'\tclass:{parent}' if parent else ''

return f'{tagname}\t/unknown\t1;"\tkind:{kind}{parent_field}{signature_field}\n'
self.tags[tag_key] = format_tag(object_name, kind, signature, parent)

def _get_safe_parameter_default_value(self, value):
"""
Expand Down Expand Up @@ -234,7 +219,7 @@ def _process_module_with_fallback_parser(self, module_filename):

kind = KIND_CLASS if tag_type_str == 'class' else KIND_FUNCTION
signature = args.strip()
self.tags[tagname] = self._format_tag(tagname, kind, signature, parent=None)
self.tags[tagname] = format_tag(tagname, kind, signature, parent=None)

def add_builtins(self):
"""
Expand All @@ -253,13 +238,7 @@ def write_to_file(self, filename):
@param filename (str)
"""
result = sorted(self.tags.values())
# write them
with open(filename, 'w') as target_file:
target_file.write(CTAGS_FILE_HEADER)
for symbol in result:
if symbol != '\n': # skip empty lines
target_file.write(symbol)
write_ctags_file(filename, self.tags.values(), sys.argv[0])


def is_import(object_, module_path):
Expand Down
70 changes: 70 additions & 0 deletions scripts/create_tags_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
#
# Author: The Geany contributors
# License: GPL v2 or later
#
# This is a helper library for Python scripts to create tag files in ctags format,
# e.g. create_py_tags.py and create_php_tags.py.
#

import datetime
import platform


CTAGS_FILE_HEADER = '''!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROGRAM_NAME {program_name} Automatically generated file - do not edit (created on {timestamp} with Python {python_version})
'''


def format_tag(tagname, kind, signature, parent, return_type=None):
"""
@param tagname (str)
@param kind (str)
@param signature (str)
@param parent (str)
@param return_type (tuple of two elements or None)
"""
if signature:
signature_field = f'\tsignature:{signature}'
else:
signature_field = ''

if parent:
parent_field = f'\tclass:{parent}'
else:
parent_field = ''

if return_type:
return_type_field = f'\ttyperef:{return_type[0]}:{return_type[1]}'
else:
return_type_field = ''

return f'{tagname}\t/unknown\t1;"\tkind:{kind}{parent_field}{signature_field}{return_type_field}\n'


def write_ctags_file(filename, tags, program_name):
"""
Sort the found tags and write them into the file specified by filename
@param filename (str)
@param tags (list)
@param program_name (str)
"""
result = sorted(tags)

ctags_file_header = CTAGS_FILE_HEADER.format(
program_name=program_name,
timestamp=datetime.datetime.now().ctime(),
python_version=platform.python_version())

with open(filename, 'w', encoding='utf-8') as target_file:
target_file.write(ctags_file_header)
for symbol in result:
if symbol != '\n': # skip empty lines
target_file.write(symbol)

0 comments on commit a6c04ed

Please sign in to comment.