GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( be6ed5...69e16c )
by thatsIch
01:06
created

TryOpenThread.__open_after_equals_sign()   A

Complexity

Conditions 3

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
1
"""Module for opening paths containing Rainmeter-specific or Windows environment variables."""
2
3
import os
4
import re
5
import threading
6
7
import sublime
8
import sublime_plugin
9
10
from . import rainmeter
11
from . import logger
12
13
14
# Files to open with sublime instead of the system default
15
SETTINGS = sublime.load_settings("Rainmeter.sublime-settings")
16
DEF_EXTS = SETTINGS.get("rainmeter_default_open_sublime_extensions", "")
17
18
if DEF_EXTS is not None:
19
    DEF_EXTS = DEF_EXTS.strip().strip(r"|").strip()
20
else:
21
    DEF_EXTS = ""
22
23
ADD_EXTS = SETTINGS.get("rainmeter_open_sublime_extensions", "")
24
25
if ADD_EXTS is not None:
26
    ADD_EXTS = ADD_EXTS.strip().strip(r"|").strip()
27
else:
28
    ADD_EXTS = ""
29
30
SUBLIME_FILES = re.compile("(?i).*\\.(" + ADD_EXTS + "|" + DEF_EXTS + ")\\b")
31
32
33
def open_path(path, transient=False):
34
    """Try to open a path.
35
36
    A path could be opened either as:
37
38
    * a file or folder path or
39
    * URL in the system default application, or
40
    * in Sublime if it's a text file.
41
42
    Use transient=True to open a file in Sublime without assigning it a tab.
43
    A tab will be created once the buffer is modified.
44
45
    Will return False if the path doesn't exist in the file system, and
46
    True otherwise.
47
    """
48
    if not path:
49
        return False
50
51
    if not os.path.exists(path):
52
        return False
53
54
    sublime.set_timeout(lambda: sublime.status_message("Opening " + path), 10)
55
    if SUBLIME_FILES.search(path):
56
        if transient:
57
            sublime.set_timeout(
58
                lambda: sublime.active_window().open_file(path,
59
                                                          sublime.TRANSIENT),
60
                10)
61
        else:
62
            sublime.set_timeout(
63
                lambda: sublime.active_window().open_file(path),
64
                10)
65
    else:
66
        os.startfile(path)
67
68
    return True
69
70
71
def open_url(url):
72
    """Try opening a url with the system default for urls.
73
74
    Will return False if it's not a url, and True otherwise.
75
    """
76
    if re.match(r"(?i)(https?|ftp)://", url.strip()):
77
        os.startfile(url)
78
        sublime.set_timeout(lambda: sublime.status_message("Opening " + url),
79
                            10)
80
        return True
81
    else:
82
        return False
83
84
85
class TryOpenThread(threading.Thread):
86
    """A wrapper method to handle threading for opening line embedded URLs."""
87
88
    def __init__(self, line, region, opn):
89
        """Construct a thread for opening URL embedded in a line."""
90
        self.line = line
91
        self.region = region
92
        self.opn = opn
93
        threading.Thread.__init__(self)
94
95
    def __find_prior_quotation_mark(self):
96
        lastquote = self.region.a - 1
97
        while lastquote >= 0 and self.line[lastquote] != "\"":
98
            lastquote -= 1
99
100
        return lastquote
101
102
    def __open_selected_text(self):
103
        # 1. Selected text
104
        selected = self.line[self.region.a:self.region.b]
105
        if self.opn(selected):
106
            logger.info("Open selected text")
107
            return True
108
109
    def __open_enclosed_string(self):
110
        # 2. String enclosed in double quotes
111
        # Find the quotes before the current point (if any)
112
        lastquote = self.__find_prior_quotation_mark()
113
114
        if not lastquote < 0 and self.line[lastquote] == "\"":
115
            # Find the quote after the current point (if any)
116
            nextquote = self.region.b
117
            while nextquote == len(self.line) or self.line[nextquote] != "\"":
118
                nextquote += 1
119
120
            if not nextquote == len(self.line) \
121
                    and self.line[nextquote] == "\"":
122
                string = self.line[lastquote: nextquote].strip("\"")
123
                if self.opn(string):
124
                    logger.info("Open string enclosed in quotes: " + string)
125
                    return True
126
127
    def __open_whitespaced_region(self):
128
        # 3. Region from last whitespace to next whitespace
129
130
        # Find the space before the current point (if any)
131
        lastspace = self.region.a - 1
132
        while lastspace >= 0 \
133
                and self.line[lastspace] != " " \
134
                and self.line[lastspace] != "\t":
135
            lastspace -= 1
136
137
        # Set to zero if nothing was found until the start of the line
138
        lastspace = max(lastspace, 0)
139
140
        if lastspace == 0 \
141
                or self.line[lastspace] == " " \
142
                or self.line[lastspace] == "\t":
143
            # Find the space after the current point (if any)
144
            nextspace = self.region.b
145
            while nextspace < len(self.line) \
146
                    and self.line[nextspace] != " " \
147
                    and self.line[nextspace] != "\t":
148
                nextspace += 1
149
150
            if nextspace >= len(self.line) \
151
                    or self.line[nextspace] == " " \
152
                    or self.line[nextspace] == "\t":
153
                string = self.line[lastspace: nextspace].strip()
154
                if self.opn(string):
155
                    logger.info("Open string enclosed in whitespace: " + string)
156
                    return True
157
158
    def __open_after_equals_sign(self):
159
        # 4. Everything after the first \"=\" until the end
160
        # of the line (strip quotes)
161
        mtch = re.search(r"=\s*(.*)\s*$", self.line)
162
        if mtch and self.opn(mtch.group(1).strip("\"")):
163
            logger.info("Open text after \"=\": " + mtch.group(1).strip("\""))
164
            return True
165
166
    def __open_whole_line(self):
167
        # 5. Whole line (strip comment character at start)
168
        stripmatch = re.search(r"^[ \t;]*?([^ \t;].*)\s*$", self.line)
169
        if self.opn(stripmatch.group(1)):
170
            logger.info("Open whole line: " + stripmatch.group(1))
171
            return
172
173
    def run(self):
174
        """Run the thread."""
175
        return self.__open_selected_text() or \
176
            self.__open_enclosed_string() or \
177
            self.__open_whitespaced_region() or \
178
            self.__open_after_equals_sign() or \
179
            self.__open_whole_line()
180
181
182
class RainmeterOpenPathsCommand(sublime_plugin.TextCommand):
183
    """Try to open paths on lines in the current selection.
184
185
    Will try to open paths to files, folders or URLs on each line in the
186
    current selection. To achieve this, the following substrings of each line
187
    intersecting the selection are tested:
188
189
    1. The string inside the selection
190
    2. The string between possible quotes preceding and following the
191
       selection, if any
192
    3. The string between the preceding and following whitespace
193
    4. Everything after the first "=" on the line until the end of the line
194
    5. The whole line, stripped of preceding semicolons
195
    """
196
197
    def __split_selection_by_new_lines(self, selection):
198
        # Split all regions into individual segments on lines (using nicely
199
        # confusing python syntax).
200
        return [
201
            j for i in [
202
                self.view.split_by_newlines(region)
203
                for region in selection
204
            ]
205
            for j in i
206
        ]
207
208
    def __open_each_line_by_thread(self, lines):
209
        """Identify segments in selected lines and tries to open them each in a new thread.
210
211
        This can be resource intensive.
212
        """
213
        fnm = self.view.file_name()
214
215
        def opn(string):
216
            """Simple callback method to apply multiple opening operations.
217
218
            An URL can be either external or internal and thus is opened differently.
219
            """
220
            opened = open_path(rainmeter.make_path(string, fnm)) or open_url(string)
221
            if opened:
222
                logger.info("found file or url '" + string + "' to open")
223
224
        for linereg in lines:
225
            wholeline = self.view.line(linereg)
226
            thread = TryOpenThread(self.view.substr(wholeline),
227
                                   sublime.Region(linereg.a - wholeline.a,
228
                                                  linereg.b - wholeline.a),
229
                                   opn)
230
            thread.start()
231
232
    def run(self, _):
233
        """Detect various scenarios of file paths and try to open them one after the other.
234
235
        :param _: _ edit unused
236
        """
237
        selection = self.view.sel()
238
        lines = self.__split_selection_by_new_lines(selection)
239
240
        loaded_settings = sublime.load_settings("Rainmeter.sublime-settings")
241
        max_open_lines = loaded_settings.get("rainmeter_max_open_lines", 40)
242
243
        # Refuse if too many lines selected to avoid freezing
244
245
        if len(lines) > max_open_lines:
246
            accept = sublime.ok_cancel_dialog(
247
                "You are trying to open " +
248
                str(len(lines)) + " lines.\n" +
249
                "That's a lot, and could take some time. Try anyway?")
250
            if not accept:
251
                return
252
253
        self.__open_each_line_by_thread(lines)
254
255
    def is_enabled(self):
256
        """Check if current syntax is rainmeter."""
257
        israinmeter = self.view.score_selector(self.view.sel()[0].a,
258
                                               "source.rainmeter")
259
260
        return israinmeter > 0
261
262
    def is_visible(self):
263
        """It is visible if it is in Rainmeter scope."""
264
        return self.is_enabled()
265