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 ( fbcf0f...5274ad )
by thatsIch
57s
created

get_addons_path()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
c 2
b 0
f 0
dl 0
loc 7
rs 9.4285
1
"""Module for getting Rainmeter-specific paths"""
2
3
import os
4
import re
5
import io
6
import getpass
7
import platform
8
import winreg
9
10
import sublime
11
import sublime_plugin
12
13
# own dependencies
14
from . import logger
15
16
from .path.program_path_provider import get_cached_program_path
17
from .path.setting_path_provider import get_cached_setting_path
18
from .path.program_drive_provider import get_cached_program_drive
19
from .path.plugin_path_provider import get_cached_plugin_path
20
from .path.addon_path_provider import get_cached_addon_path
21
22
from .completion.completion import ContextSensAutoCompletion
23
24
25
def skins_path():
26
    """Get the cached value of the #SKINSPATH# variable"""
27
28
    return _skins_path
29
30
31
def get_skins_path():
32
    """Get the value of the #SKINSPATH# variable"""
33
34
    # First try to load the value from the "rainmeter_skins_path" setting
35
    loaded_settings = sublime.load_settings("Rainmeter.sublime-settings")
36
    skinspath = loaded_settings.get("rainmeter_skins_path", None)
37
38
    # if it's found, return it
39
    # We trust the user to enter something meaningful here
40
    # and don't check anything.
41
    if skinspath:
42
        logger.info(__file__, "get_skins_path", "Skins path found in sublime-settings file.")
43
        return os.path.normpath(skinspath) + "\\"
44
45
    # If it's not set, try to detect it automagically
46
47
    rainmeterpath = get_cached_program_path()
48
    if not rainmeterpath:
49
        return
50
51
    settingspath = get_cached_setting_path()
52
    if not settingspath:
53
        return
54
55
    # First, try to read the SkinPath setting from Rainmeter.ini
56
    fhnd = io.open(os.path.join(settingspath, "Rainmeter.ini"))
57
    lines = fhnd.read()
58
    fhnd.close()
59
60
    # Find the skinspath setting in the file
61
    match = re.search(r"""(?imsx)
62
63
                     # Find the first [Rainmeter] section
64
                     (^\s*\[\s*Rainmeter\s*\]\s*$)
65
                     (.*?
66
67
                         # Find the "SkinPath" and "="
68
                         (^\s*SkinPath\s*=\s*
69
70
                             # Read until the next line ending and store
71
                             # in named group
72
                             (?P<skinpath>[^$]+?)\s*?$
73
                         )
74
                     ).*?
75
76
                     # All of this needs to happen before the next section
77
                     (?:^\s*\[\s*[^\[\]\s]+\s*\]\s*$)
78
                     """, lines)
79
80
    # if skinspath setting was found, return it
81
    if match:
82
        logger.info(__file__, "get_skins_path", "Skins path found in Rainmeter.ini.")
83
        return match.group("skinpath").strip().replace("/", "\\")
84
85
    # if it's not found in the settings file, try to guess it
86
87
    # If program path and setting path are equal, we have a portable
88
    # installation. In this case, the Skins folder is inside the rainmeter
89
    # path
90
    if os.path.samefile(rainmeterpath, settingspath):
91
        logger.info(__file__, "get_skins_path", "Skin path found in #PROGRAMPATH#" +
92
                    " because portable installation")
93
        return os.path.join(rainmeterpath, "Skins") + "\\"
94
95
    # If it's not a portable installation, we try looking into the "My
96
    # Documents" folder Since it could be relocated by the user, we have to
97
    # query its value from the registry
98
    try:
99
        regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
100
                                r"Software\Microsoft\Windows" +
101
                                r"\CurrentVersion\Explorer" +
102
                                r"\User Shell Folders")
103
        keyval = winreg.QueryValueEx(regkey, "Personal")
104
105
        pathrep = keyval[0]
106
107
        # The path could (and most likely, will) contain environment
108
        # variables that have to be expanded first
109
        pathrep = os.path.expandvars(pathrep)
110
111
        logger.info(__file__, "get_skins_path", "Guessed Skin path from My Documents" +
112
                    " location in registry")
113
        return os.path.join(pathrep, "Rainmeter\\Skins") + "\\"
114
115
    except OSError:
116
        pass
117
118
    # If the value could not be retrieved from the registry,
119
    # we try some educated guesses about default locations
120
    try:
121
        username = getpass.getuser()
122
    except Exception:
123
        logger.info(__file__, "get_skins_path", "Skins path could not be located." +
124
                    " Please set the \"skins_path\" setting in your Rainmeter" +
125
                    " settings file.")
126
        return
127
    else:
128
        # check if windows version is XP
129
        winversion = platform.version()
130
        if int(winversion[0]) < 6:
131
            mydocuments = os.path.join("C:\\Documents and Settings",
132
                                       username,
133
                                       "My Documents") + "\\"
134
135
            logger.info(__file__, "get_skins_path", "Found Windows XP or lower." +
136
                        " Skins path assumed to be " + mydocuments +
137
                        "Rainmeter\\Skins\\")
138
        else:
139
            mydocuments = os.path.join("C:\\Users",
140
                                       username,
141
                                       "Documents") + "\\"
142
143
            logger.info(__file__, "get_skins_path", "Found Windows Vista or higher." +
144
                        " Skins path assumed to be " + mydocuments +
145
                        "Rainmeter\\Skins\\")
146
147
        logger.info(__file__, "get_skins_path", "Skin path guessed from user name" +
148
                    " and Windows version")
149
        return os.path.join(mydocuments, "Rainmeter\\Skins") + "\\"
150
151
152
def get_current_path(filepath):
153
    """Get the value of the #CURRENTPATH# variable for the specified path.
154
155
    Returns None if the file path is not in the skins folder
156
157
    """
158
159
    filepath = os.path.normpath(filepath)
160
161
    skinspath = skins_path()
162
    if not skinspath or not filepath.startswith(skinspath):
163
        logger.info(__file__, "get_current_path", "current path could not be found because" +
164
                    " either the skins path could not be found or the current file" +
165
                    " is not located in the skins path.")
166
        return
167
168
    if os.path.isfile(filepath):
169
        return os.path.dirname(filepath) + "\\"
170
    else:
171
        return filepath + "\\"
172
173
174
def get_root_config_path(filepath):
175
    """Get the value of the #ROOTCONFIGPATH# variable for the specified path
176
177
    Returns None if the path is not in the skins folder
178
179
    """
180
181
    filepath = os.path.normpath(filepath)
182
183
    skinspath = skins_path()
184
    if not skinspath or not filepath.startswith(skinspath):
185
        logger.info(__file__, "get_root_config_path", "root config path could not be found" +
186
                    " because either the skins path could not be found or the" +
187
                    " current file is not located in the skins path.")
188
        return
189
190
    relpath = os.path.relpath(filepath, skinspath)
191
    logger.info(__file__, "get_root_config_path",
192
                os.path.join(skinspath, relpath.split("\\")[0]) + "\\")
193
194
    return os.path.join(skinspath, relpath.split("\\")[0]) + "\\"
195
196
197
def get_current_file(filepath):
198
    """Get the value of the #CURRENTFILE# variable for the specified path
199
200
    Returns None if the path is not in the skins folder
201
202
    """
203
204
    filepath = os.path.normpath(filepath)
205
206
    skinspath = skins_path()
207
    if not skinspath or not filepath.startswith(skinspath):
208
        logger.info(__file__, "get_current_file", "current file could not be found because" +
209
                    " either the skins path could not be found or the current" +
210
                    " file is not located in the skins path.")
211
        return
212
213
    if os.path.isfile(filepath):
214
        return os.path.basename(filepath)
215
    else:
216
        logger.info(__file__, "get_current_file", "specified path is not a file.")
217
        return
218
219
220
def get_current_config(filepath):
221
    """Get the value of the #CURRENTCONFIG# variable for the specified path
222
223
    Returns None if the path is not in the skins folder
224
225
    """
226
227
    filepath = os.path.normpath(filepath)
228
229
    skinspath = skins_path()
230
    if not skinspath or not filepath.startswith(skinspath):
231
        logger.info(__file__, "get_current_config", "current config could not be found" +
232
                    " because \either the skins path could not be found or the" +
233
                    " current file is not located in the skins path.")
234
        return
235
236
    if os.path.isfile(filepath):
237
        filepath = os.path.dirname(filepath)
238
239
    return os.path.relpath(filepath, skinspath)
240
241
242
def get_resources_path(filepath):
243
    """Get the value of the #@# variable for the specified path
244
245
    Returns None if the path is not in the skins folder
246
247
    """
248
249
    rfp = get_root_config_path(filepath)
250
251
    if not rfp:
252
        return
253
    logger.info(__file__, "get_resources_path", os.path.join(rfp, "@Resources") + "\\")
254
    return os.path.join(rfp, "@Resources") + "\\"
255
256
257
def replace_variables(string, filepath):
258
    """Replace Rainmeter built-in variables and Windows environment variables
259
    in string.
260
261
    Replaces occurrences of the following variables in the string:
262
    #CURRENTFILE#
263
    #CURRENTPATH#
264
    #ROOTCONFIGPATH#
265
    #CURRENTCONFIG#
266
    #@#
267
    #SKINSPATH#
268
    #SETTINGSPATH#
269
    #PROGRAMPATH#
270
    #PROGRAMDRIVE#
271
    #ADDONSPATH#
272
    #PLUGINSPATH#
273
    Any Windows environment variables (like %APPDATA%)
274
    filepath must be a skin file located in a subdirectory of the skins folder
275
276
    """
277
278
    # lambdas for lazy evaluation
279
    variables = {"#CURRENTFILE#": lambda: get_current_file(filepath),
280
                 "#CURRENTPATH#": lambda: get_current_path(filepath),
281
                 "#ROOTCONFIGPATH#": lambda: get_root_config_path(filepath),
282
                 "#CURRENTCONFIG#": lambda: get_current_config(filepath),
283
                 "#@#": lambda: get_resources_path(filepath),
284
                 "#SKINSPATH#": lambda: skins_path(),
285
                 "#SETTINGSPATH#": lambda: get_cached_setting_path(),
286
                 "#PROGRAMPATH#": lambda: get_cached_program_path(),
287
                 "#PROGRAMDRIVE#": lambda: get_cached_program_drive(),
288
                 "#ADDONSPATH#": lambda: get_cached_addon_path(),
289
                 "#PLUGINSPATH#": lambda: get_cached_plugin_path()}
290
291
    pattern = re.compile("(?i)" + "|".join(list(variables.keys())))
292
    # replace Rainmeter variables
293
    repl = pattern.sub(lambda x: variables[x.group().upper()](),
294
                       string)
295
    # expand windows environment variables
296
    repl = os.path.expandvars(repl)
297
    return repl
298
299
300
def make_path(string, filepath):
301
    """Make the string into an absolute path of an existing file or folder,
302
303
    replacing Rainmeter built-in variables relative to the file specified in
304
    filepath (see replace_variables()) will return None if the file or folder
305
    doesn't exist, or if string is None or empty.
306
307
    """
308
309
    if not string:
310
        return None
311
312
    repl = replace_variables(string, filepath)
313
    norm = os.path.normpath(repl)
314
315
    # For relative paths, try folder of current file first
316
317
    if not os.path.isabs(norm):
318
        curpath = get_current_path(filepath)
319
        if curpath:
320
            abso = os.path.join(curpath, norm)
321
        else:
322
            abso = os.path.join(os.path.dirname(filepath), norm)
323
324
        if os.path.exists(abso):
325
            return abso
326
327
        # if that doesn't work, try relative to skins path
328
        # (for #CURRENTCONFIG#)
329
        abso = os.path.join(skins_path(), norm)
330
        if os.path.exists(abso):
331
            return abso
332
    # for absolute paths, try opening containing folder if file does not exist
333
    else:
334
        if os.path.exists(norm):
335
            return norm
336
337
        if os.path.exists(os.path.dirname(norm)):
338
            return os.path.dirname(norm)
339
340
    return
341
342
343
# Initialize Module
344
# Global Variables
345
settings = None
346
347
_skins_path = None
348
349
350
# Called automatically from ST3 if plugin is loaded
351
# Is required now due to async call and ignoring sublime.* from main routine
352
def plugin_loaded():
353
    # define variables from the global scope
354
    global settings
355
356
    global _skins_path
357
358
    settings = sublime.load_settings("Rainmeter.sublime-settings")
359
360
    # Cache the paths
361
    _skins_path = get_skins_path()
362
363
    logger.info(__file__, "plugin_loaded()", "#PROGRAMPATH#:\t\t" + get_cached_program_path())
364
    logger.info(__file__, "plugin_loaded()", "#PROGRAMDRIVE#:\t" + get_cached_program_drive())
365
    logger.info(__file__, "plugin_loaded()", "#SETTINGSPATH#:\t" + get_cached_setting_path())
366
    logger.info(__file__, "plugin_loaded()", "#SKINSPATH#:\t\t" + skins_path())
367
    logger.info(__file__, "plugin_loaded()", "#PLUGINSPATH#:\t\t" + get_cached_plugin_path())
368
    logger.info(__file__, "plugin_loaded()", "#ADDONSPATH#:\t\t" + get_cached_addon_path())
369
370
371
class MeterAutoComplete(sublime_plugin.EventListener):
372
373
    # only show our completion list because nothing else makes sense in this context
374
    flags = sublime.INHIBIT_EXPLICIT_COMPLETIONS | sublime.INHIBIT_WORD_COMPLETIONS
375
    scope = "source.rainmeter"
376
377
    comment_exp = re.compile(r'^\s*;.*')
378
    meter_exp = re.compile(r'^\s*')
379
380
    completions = [
381
        # measures
382
        (re.compile(r'^\s*Measure\s*=\s*'), [
383
            # key, value
384
            ["Calc", "Calc"],
385
            ["CPU", "CPU"],
386
            ["FreeDiskSpace", "FreeDiskSpace"],
387
            ["Loop", "Loop"],
388
389
            # memory measure
390
            ["Memory", "Memory"],
391
            ["PhysicalMemory", "PhysicalMemory"],
392
            ["SwapMemory", "SwapMemory"],
393
394
            # net measure
395
            ["NetIn", "NetIn"],
396
            ["NetOut", "NetOut"],
397
            ["NetTotal", "NetTotal"],
398
399
            ["Plugin", "Plugin"],
400
            ["Registry", "Registry"],
401
            ["Script", "Script"],
402
            ["String", "String"],
403
            ["Time", "Time"],
404
            ["Uptime", "Uptime"]
405
        ]),
406
407
        # meters
408
        (re.compile(r'^\s*Meter\s*=\s*'), [
409
            # key, value
410
            ["Bar", "Bar"],
411
            ["Bitmap", "Bitmap"],
412
            ["Button", "Button"],
413
            ["Histogram", "Histogram"],
414
            ["Image", "Image"],
415
            ["Line", "Line"],
416
            ["Rotator", "Rotator"],
417
            ["Roundline", "Roundline"],
418
            ["Shape", "Shape"],
419
            ["String", "String"]
420
        ]),
421
        # general options
422
423
        # bar
424
        # bar orientation
425
        (re.compile(r'^\s*BarOrientation\s*=\s*'), [
426
            # key, value
427
            ["Horizontal", "Horizontal"],
428
            ["Vertical\tDefault", "Vertical"]
429
        ]),
430
431
        # bar flip
432
        (re.compile(r'^\s*Flip\s*=\s*'), [
433
            # key, value
434
            ["0\tDefault", "0"],
435
            ["1\tBar is flipped", "1"]
436
        ]),
437
438
        # bitmap
439
440
        # button
441
        # histogram
442
        # image
443
        # line
444
        # rotator
445
        # roundline
446
        # shape
447
        # string
448
449
        # plugins
450
        (re.compile(r'^\s*Plugin\s*=\s*'), [
451
            # key, value
452
            ["ActionTimer", "ActionTimer"],
453
            ["AdvancedCPU", "AdvancedCPU"],
454
            ["AudioLevel", "AudioLevel"],
455
            ["CoreTemp", "CoreTemp"],
456
            ["FileView", "FileView"],
457
            ["FolderInfo", "FolderInfo"],
458
            ["InputText", "InputText"],
459
            ["iTunes", "iTunesPlugin"],
460
            ["MediaKey", "MediaKey"],
461
            ["NowPlaying", "NowPlaying"],
462
            ["PerfMon", "PerfMon"],
463
            ["Ping", "PingPlugin"],
464
            ["Power", "PowerPlugin"],
465
            ["Process", "Process"],
466
            ["Quote", "QuotePlugin"],
467
            ["RecycleManager", "RecycleManager"],
468
            ["ResMon", "ResMon"],
469
            ["RunCommand", "RunCommand"],
470
            ["SpeedFan", "SpeedFanPlugin"],
471
            ["SysInfo", "SysInfo"],
472
            ["WebParser", "WebParser"],
473
            ["WiFiStatus", "WiFiStatus"],
474
            ["Win7Audio", "Win7AudioPlugin"],
475
            ["WindowMessage", "WindowMessagePlugin"]
476
        ]),
477
    ]
478
479
    def on_query_completions(self, view, prefix, locations):
480
        for location in locations:
481
            # checks if the current scope is correct so it is only called in the files with the correct scope
482
            # here is scope only rainmeter files
483
            if view.match_selector(location, self.scope):
484
                # find last occurance of the [] to determine the ini sections
485
                line = view.line(location)
486
                line_contents = view.substr(line)
487
488
                # starts with Measure, followed by an equal sign
489
                for exp, elements in self.completions:
490
                    if exp.search(line_contents):
491
                        return elements, self.flags
492
        return None
493
494
495
class CompletionProxy(sublime_plugin.EventListener):
496
497
    proxied_completion = None
498
499
    def __init__(self):
500
        self.proxied_completion = ContextSensAutoCompletion()
501
502
    def on_query_completions(self, view, prefix, locations):
503
        return self.proxied_completion.on_query_completions(view, prefix, locations)
504