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 ( 28ac24...6b7ebe )
by thatsIch
01:00
created

get_plugins_path()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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