File: open_file.py

package info (click to toggle)
gnat-gps 18-5
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 45,716 kB
  • sloc: ada: 362,679; python: 31,031; xml: 9,597; makefile: 1,030; ansic: 917; sh: 264; java: 17
file content (159 lines) | stat: -rw-r--r-- 4,875 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""Provides a contextual menu in editors that opens the source file referenced
at the cursor's position.
The file's location may be absolute or relative to the projects source
folders. The file name can be followed by ":" and a line number, to go to
that specific line number.

This contextual menu will work with:
  - include clauses in C-headerfiles
  - Absolute references in the sources.

If the file name includes spaces, you should first select the file name. This
will skip the automatic detection of file name, and take the whole selection
has a file name if such a file is found on the disk.
"""

import GPS
import re
import gps_utils
import os
from text_utils import get_selection_or_line


file_pattern = \
    u'((?:[a-zA-Z]:)?(?:[\\\\/]?[\w\d._$-]+)+)(?::(\d+)(?::(\d+))?)?'
# The regexp pattern to search file file:line:column references on the
# current line.

std_include_path = ["/usr/include", "/usr/local/include"]
# Standard search paths for files

# __sep = u'[:\'\" <>*?]'
# file_pattern = "(?:^|" + __sep + ")" \
#  + u'((?:[/\\]?[\w\d._]+)+)(?::(\d+)(?::(\d+))?)?' \
#  + "(?:$|" + __sep + ")"
# A second version of the pattern which only matches inside specific
# separators (or beginning/end of line)

############################################################################
# No user customization below this line
############################################################################

file_pattern_re = re.compile(file_pattern)


class __contextData(object):
    pass


def __filter(context):
    """Checks whether the contextual menu should be displayed"""
    if context.file() is None:
        return False

    ed = GPS.EditorBuffer.get(open=False)
    if not ed:
        return False

    try:
        (ed, start, end) = get_selection_or_line(ed, context.location())
    except:
        return False  # No file information in the context
    text = ed.get_chars(start, end)

    data = __contextData()
    context.open_file = data
    data.file = ""
    data.line = 0
    data.column = 0

    cursor_col = context.location().column()

    if ed.selection_end() != ed.selection_start():
        data.file = text   # No post-processing
    else:
        # Try to find the filename we clicked on
        pos = 0
        while pos < len(text):
            m = file_pattern_re.search(text, pos)
            if m and m.start() <= cursor_col and m.end() >= cursor_col:
                data.file = m.group(1)
                if m.group(2):
                    data.line = int(m.group(2))
                if m.group(3):
                    data.column = int(m.group(3))
                break
            elif m:
                pos = m.end()
            else:
                return False

    if data.file == "":
        return False

    if os.path.exists(data.file):
        return True
    else:
        # Let GPS search in all source dirs and predefined paths
        f = GPS.File(data.file)
        if os.path.exists(f.path):
            data.file = f.path
            return True

        # Search with just the basename (otherwise "src/file.c" where
        # "src/" is a source_dir would not be found)
        f = GPS.File(os.path.basename(data.file))
        if os.path.exists(f.path):
            data.file = f.path
            return True

        # One more try, include standard include paths for C files
        for p in std_include_path:
            f = os.path.join(p, data.file)
            if os.path.exists(f):
                data.file = f
                return True

        # Special case for C files: #include accepts directories that are
        # not necessarily in the source dirs

        # Handle the case where the include statement contains a directory.
        if os.path.splitext(data.file)[1] in \
                [".h", ".hh", ".cfg", ".c", ".gen"]:
            for p in GPS.Project.root().source_dirs(True):
                f = os.path.join(p, data.file)
                if os.path.exists(f):
                    data.file = f
                    return True

    return False


def __label(context):
    """Returns the label to use for the contextual menu"""
    data = context.open_file
    return "Open <b>" + os.path.basename(data.file) + "</b>"


@gps_utils.interactive(
    name='open file at cursor location',
    contextual=__label,
    filter=__filter,
    static_path="Open file")
def __activate():
    """
    Open the file specified at the cursor's location, for instance in a C
    import statement.
    Line numbers are also analyzed when possible ("file:line")
    """
    context = GPS.contextual_context()
    data = context.open_file

    try:
        ed = GPS.EditorBuffer.get(GPS.File(data.file))
        view = ed.current_view()
        loc = ed.at(line=data.line, column=data.column)
        view.goto(loc)
        GPS.MDI.get_by_child(view).raise_window()
    except:
        pass