Get file syntax selection in sublime text 3 plugin - python

I have a very small plugin to open a perl file module starting from the use statement. It's really basic and it just replaces '::' with '/' and then if the file exists in one of the paths specified in PERL5LIB, it opens it.
I want it to run only when the open file syntax is selected as perl.
Is there any API to get that information?
This is the code that I have now:
class OpenPerlModule(sublime_plugin.TextCommand):
def run(self, edit=None, url=None):
perl_file = url.replace("::", "/")
perl_dirs = os.environ.get('PERL5LIB')
for perl_dir in perl_dirs.split(':'):
if (os.path.exists(perl_dir + '/' + perl_file + '.pm')):
self.view.window().open_file(perl_dir + '/' + perl_file + '.pm')
return
(The OS is Ubuntu)

Here is the code snippet you're looking for
self.view.settings().get("syntax")
You should check whether it's a syntax related to Perl or not. I suggest something like this:
syntax = self.view.settings().get("syntax")
syntax.endswith("Perl.tmLanguage") or syntax.endswith("Perl.sublime-syntax")
The second or clause is to cover the new syntax that's introduced in >=3080

Aside from self.view.settings().get("syntax") as described in Allen Bargi's answer, you could also get the scope for the current cursor position and check for source.perl in it:
import sublime_plugin
class FindScopeCommand(sublime_plugin.TextCommand):
def run(self, edit):
# `sel()` returns a list of Regions that are selected.
# Grab the beginning point of the first Region in the list.
first_point = self.view.sel()[0].a
# now, get the full scope name for that point
scope = self.view.scope_name(first_point)
if "source.perl" in scope:
print("You're using Perl. Yay!")
else:
print("Why don't you love Perl?")

Related

How can I make these functions work following OOP Principles?

I have several functions/methods in a class that are kind of connected. I am building a class that mimics terminal commands and links. However, someone told me this is not proper OOP. How can I separate these methods to work independently. Methods shouldn't call other methods. Correct?
class directory:
#FILES, LINKS AND DIRECTORIES
current_path = []
hold_files = [
'test1.txt', 'test2.py',
{'/desktop': ['computer.txt','tfile.doc',
{'/peace':{
'/pictures': [
'hello.gif',
'smile.gif',
'run.gif'
]},
'/work':[
'file1.txt',
'file2.txt'
]
}]
}
]
#recursively delete folder (if dot in)
def delete(itself):
#if dictionary, call self, else delete
del itself
return
## HELPER METHODS
# Join list together to produce new link, basically return the added folder to the link
def concatenate(self):
new_link ="".join(current_path)
return new_link
#strip slashes and place in list
def adjust_link(self, paths):
new_string = ""
# shorten link, if someone uses cd .., basically go back to previous folder
if paths == "cd ..":
current_path.pop()
#extend link, if someone is cding into another folder, remove /'s and append to separate list
elif "cd " in paths:
paths = paths[3:]
for slash in paths:
if slash == "/":
current.append(new_string)
new_string = ""
else:
new_string+=slash
# This shouldn't be here as OOP must be separated but this calls the other function to concatenate a new link
stripped = concatenate()
return stripped
#returns link
def link(self, paths):
address_location = adjust_link(paths)
return address_location
directory.link("cd desktop/peace")
directory.link("cd pictures")
directory.link("cd ..")
directory.delete()
Thank you.
*Also, this is not a refactoring question. I already asked on stack exchange code review and they told me to come here. Code does not work.
Edit 2: why won't "directory.link()" work?
"I have a program here and I want to convert it to OOP" is not usually how it's done: "I have a problem I want to solve using OOP" is usually the approach. It looks like you are creating something that will traverse an internal directory structure. So a skeleton might look like:
class DirectoryTraverser:
def __init__(self, directory_tree):
self.hold_files = directory_tree
self.current_path = []
def... # all your other functions
# then to use it might look like:
# create a directory traversal object with directory tree
dt = DirectoryTraverser(hold_files)
dt.link("cd desktop/peace")
dt.link("cd pictures")
dt.link("cd ..")
dt.delete()

How to solve class objecto has no atribute

beginner Python user here.
So, I´m trying to make a program that orders the files of my (many) Downloads folder.
I made a class object to work with the many folders:
class cContenedora:
def __int__(self, nCarp, dCarp): #nCarp Stands is the file name and dCarp Stands for file directory.
self.nCarp = nCarp
self.dCarp = dCarp
So, y wrote a instance like this:
Download = cContenedora()
Download.nCarp = "Downloads/"
#The side bar is for making a path to move my archives from with shutil.move(path, dest)
Download.dCarp = "/Users/MyName/Download/"
#This is for searching the folder with os.listdir(Something.dCarp)
Then, I wrote my function, and it goes something like this:
def ordenador(carpetaContenedora, formato, directorioFinal): #carpetaContenedora is a Download Folder
carpetaContenedora = cContenedora() #carpetaContenedora one of the class objects
dirCCont = os.listdir(carpetaContenedora.dCarp) #The to directory is carpetaContenedora.cCarp
for a in dirCCont:
if a.endswith(formato):
path = "/Users/Aurelio Induni/" + carpetaContenedora().nCarp + a
try:
shutil.move(path, directorioFinal)
print(Fore.GREEN + a + "fue movido exitosamente.")
except:
print(Fore.RED + "Error con el archivo" + a)
pass
for trys in range(len(listaCarpetasDestino)-1): #Is a list full of directories.
for container in listaCarpetasFuente: #A short list of all my Downloads Folder.
for formatx in listaFormatos: #listaFormatos is a list ful of format extensions like ".pdf"
#try: #I disabled this to see the error istead of "Error Total"
ordenador(container, formatx, listaCarpetasDestino[trys])
#except:
#print(Fore.RED + "Error Total") #I disabled this to see the error.
But every time I run it I get the following:
AttributeError: 'cContenedora' object has no attribute 'dCarp'
It says the error is in line 47 (the one with the os.listdir(carpetaContenedora.dCarp))
I´m sure is something small. Python is so amazing, but it also can be so frustrating not knowing what´s wrong.
There is a spelling mistake in the initialization of your instance. It should be "init" instead of "int".
In the class cContenedora, the function should be
class cContenedora:
def __init__(self, nCarp, dCarp):
self.nCarp = nCarp
self.dCarp = dCarp
Additionally, When you are passing in the parameter. Make sure to pass in both of the parameters in the line with Value.
CContenedora(nCarp="something",dCarp="something")
Your class initializer, i.e., __init__() function has 2 parameters nCarp and dCarp but when you are actually creating the object there are no parameters passed.
Your function ordenador takes the first parameter as carpetaContenedora, on the first line same variable is assigned a new object of cContenedora, at this line the original values you passed are lost forever.
This could be the reason it is giving for the error.
Refer this link for more details on how to create classes and instantiate the object.

Sublime Text 3 Plugin changing run to on_pre_save

I have been working on a Sublime Text 3 plugin that fixes some coding standards I have at work(That I have a bad habit of missing) I currently have this working with a command run in the console. Most of the code was originally from this thread.
import sublime, sublime_plugin
class ReplaceCommand(sublime_plugin.TextCommand):
def run(self, edit):
#for each selected region
region = sublime.Region(0, self.view.size())
#if there is slected text
if not region.empty():
#get the selected region
s = self.view.substr(region)
#perform the replacements
s = s.replace('){', ') {')
s = s.replace('}else', '} else')
s = s.replace('else{', 'else {')
#send the updated string back to the selection
self.view.replace(edit, region, s)
Then you just need to run:
view.run_command('replace')
And it will apply the coding standards(there are more I plan to implement but for now i'll stick with these) I would like this to run on save.
I tried just changing run(self, edit) to on_pre_save(self, edit) but it does not work. I don't get any syntax errors but It just doesn't work.
Can anyone tell me how to make this run on save instead of having to run the command?
On ST3 the only way to get an Edit object is by running a TextCommand. (It's in the docs, but they're not terribly clear). But, fortunately, you can run the command pretty much the same way you have been doing.
Events handlers, like on_pre_save, can only be defined on an EventListener. The on_pre_save() event is passed a view object so you just need to add something like this, which kicks-off the command you've already written.
class ReplaceEventListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
view.run_command('replace')
The Solutions I came to was to create an on_pre_save() function that runs the command I listed earlier:
import sublime, sublime_plugin
class ReplaceCodeStandardsCommand(sublime_plugin.TextCommand):
def run(self, edit):
#for each selected region
region = sublime.Region(0, self.view.size())
#if there is slected text
if not region.empty():
#get the selected region
s = self.view.substr(region)
#perform the replacements
s = s.replace('){', ') {')
s = s.replace('}else', '} else')
s = s.replace('else{', 'else {')
#send the updated string back to the selection
self.view.replace(edit, region, s)
class ReplaceCodeStandardsOnSave(sublime_plugin.EventListener):
# Run ST's 'unexpand_tabs' command when saving a file
def on_pre_save(self, view):
if view.settings().get('update_code_standard_on_save') == 1:
view.window().run_command('replace_code_standards')
Hopefully this code helps someone!

Call a function through a variable in Python

I'm trying to make a game engine in Python as a "weekend project" but I have a problem.
I'm trying to make it that a user can declare a keybind by typing the key and the function into a text file what they want to run but when I run the code from that sheet with exec it runs the function and I have no idea on how to call the function through the variable. (Also I don't want it to run the function when it runs the code.)
Here is the code I use for executing the code from binds.zdata
for line in bind:
try:
exec line
except:
try:
error.write(localtime + " : " + "ERROR: Could not bind key from the bind file : " + line)
except:
pass
Here's the text in binds.zdata
_w = Functions.motion.move().forward()
_a = Functions.motion.move().left()
_s = Functions.motion.move().back()
_d = Functions.motion.move().right()
You want to lose the () at the end of each line:
_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right
now you can call the function through the variable by simply applying parenthesis, such as:
_w()
I'm not sure I recommend "exec" since somebody could put any code in there.
But here is your problem.
_w = Functions.motion.move().forward()
Calls the function 'forward' and puts the results in _w.
_w = Functions.motion.move().forward
Assigns the function 'forward' to the variable '_w'.
Since you asked what I would do, I would create a set of tokens that represent the various functions and then let them do the mapping in side a config file (see: ConfigParser). Then parse the config file. It is a little more work, but a lot more secure.
I'd first change the lines in the binds.zdata file like this so they assign a function to each of the variables:
_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right
Then, assuming that Functions is defined, I'd execute all the lines in the file at once with something like:
try:
execfile('binds.zdata')
except Exception as exc:
error.write(localtime + " : " + "ERROR: executing binds file")
Afterwards, the _w, _a, _s, _d variables will have the desired function assigned to them, which you can call as usual — i.e. _w(), _a(), etc.
Reading the config like that with exec is going to open you to all kinds of pain at runtime, as the user's config file is going to be executable code that you are just trusting. Malicious or unintentionally just bad commands will get execute. You could template it like this:
def read_config(config_file):
user_button_map = {}
with open(config_file, 'r') as fh:
for line in fh.readlines():
key,func_key = line.strip().split(',')
assert(key in BUTTON_MAP_FUNCTIONS),"no such action"
user_button_map[key] = BUTTON_MAP_FUNCTIONS[func_key]
return user_button_map
BUTTON_MAP_FUNCTIONS = {
"forward" : Functions.motion.move().forward,
"left" : Functions.motion.move().left,
# etc...
}
# sample config file
# w, forward
# a, left
# etc...

Substitutions inside links in reST / Sphinx

I am using Sphinx to document a webservice that will be deployed in different servers. The documentation is full of URL examples for the user to click and they should just work. My problem is that the host, port and deployment root will vary and the documentation will have to be re-generated for every deployment.
I tried defining substitutions like this:
|base_url|/path
.. |base_url| replace:: http://localhost:8080
But the generated HTML is not what I want (doesn't include "/path" in the generated link):
http://localhost:8080/path
Does anybody know how to work around this?
New in Sphinx v1.0:
sphinx.ext.extlinks – Markup to shorten external links
https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
The extension adds one new config value:
extlinks
This config value must be a dictionary of external sites, mapping unique short alias names to a base URL and a prefix. For example, to create an alias for the above mentioned issues, you would add
extlinks = {'issue':
('http://bitbucket.org/birkenfeld/sphinx/issue/%s', 'issue ')}
Now, you can use the alias name as a new role, e.g. :issue:`123`. This then inserts a link to http://bitbucket.org/birkenfeld/sphinx/issue/123. As you can see, the target given in the role is substituted in the base URL in the place of %s.
The link caption depends on the second item in the tuple, the prefix:
If the prefix is None, the link caption is the full URL.
If the prefix is the empty string, the link caption is the partial URL given in the role content (123 in this case.)
If the prefix is a non-empty string, the link caption is the partial URL, prepended by the prefix – in the above example, the link caption would be issue 123.
You can also use the usual “explicit title” syntax supported by other roles that generate links, i.e. :issue:`this issue <123>`. In this case, the prefix is not relevant.
I had a similar problem where I needed to substitute also URLs in image targets.
The extlinks do not expand when used as a value of image :target: attribute.
Eventually I wrote a custom sphinx transformation that rewrites URLs that start with a given prefix, in my case, http://mybase/. Here is a relevant code for conf.py:
from sphinx.transforms import SphinxTransform
class ReplaceMyBase(SphinxTransform):
default_priority = 750
prefix = 'http://mybase/'
def apply(self):
from docutils.nodes import reference, Text
baseref = lambda o: (
isinstance(o, reference) and
o.get('refuri', '').startswith(self.prefix))
basetext = lambda o: (
isinstance(o, Text) and o.startswith(self.prefix))
base = self.config.mybase.rstrip('/') + '/'
for node in self.document.traverse(baseref):
target = node['refuri'].replace(self.prefix, base, 1)
node.replace_attr('refuri', target)
for t in node.traverse(basetext):
t1 = Text(t.replace(self.prefix, base, 1), t.rawsource)
t.parent.replace(t, t1)
return
# end of class
def setup(app):
app.add_config_value('mybase', 'https://en.wikipedia.org/wiki', 'env')
app.add_transform(ReplaceMyBase)
return
This expands the following rst source to point to English wikipedia.
When conf.py sets mybase="https://es.wikipedia.org/wiki" the links would point to the Spanish wiki.
* inline link http://mybase/Helianthus
* `link with text <http://mybase/Helianthus>`_
* `link with separate definition`_
* image link |flowerimage|
.. _link with separate definition: http://mybase/Helianthus
.. |flowerimage| image:: https://upload.wikimedia.org/wikipedia/commons/f/f1/Tournesol.png
:target: http://mybase/Helianthus
Ok, here's how I did it. First, apilinks.py (the Sphinx extension):
from docutils import nodes, utils
def setup(app):
def api_link_role(role, rawtext, text, lineno, inliner, options={},
content=[]):
ref = app.config.apilinks_base + text
node = nodes.reference(rawtext, utils.unescape(ref), refuri=ref,
**options)
return [node], []
app.add_config_value('apilinks_base', 'http://localhost/', False)
app.add_role('apilink', api_link_role)
Now, in conf.py, add 'apilinks' to the extensions list and set an appropriate value for 'apilinks_base' (otherwise, it will default to 'http://localhost/'). My file looks like this:
extensions = ['sphinx.ext.autodoc', 'apilinks']
# lots of other stuff
apilinks_base = 'http://host:88/base/'
Usage:
:apilink:`path`
Output:
http://host:88/base/path
You can write a Sphinx extension that creates a role like
:apilink:`path`
and generates the link from that. I never did this, so I can't help more than giving this pointer, sorry. You should try to look at how the various roles are implemented. Many are very similar to what you need, I think.

Categories