Say if I have the following code in Sublime:
if (condition) {
// code
}
When my cursor is at the end of // code, I would like to set a key bind (e.g. Tab) that will exit the if-statement block and move it to the end of }. Thanks.
The BracketHighlighter plugin can provide this functionality natively... sort of. In its example shortcuts file, Example.sublime-keymap, there is a "Go to Right Bracket" example key binding:
// Go to right bracket
{
"keys": ["ctrl+alt+super+down"],
"command": "bh_key",
"args":
{
"lines" : true,
"plugin":
{
"type": ["__all__"],
"command": "bh_modules.bracketselect",
"args": {"select": "right"}
}
}
},
The only problem is that the called bracketselect command moves the cursor to the left side of the right bracket, requiring another keypress to fully escape from the block. I don't think that's what you want.
Worry not! Thankfully, BracketHighlighter provides a very intuitive plugin API, and I found that I could modify the bracketselect plugin to create a command that would escape from a bracket-enclosed block—basically the same as bracketselect, but it moves the cursor to the right side of the closing bracket rather than the left, and doesn't need any extra arguments.
You'll first need to install BracketHighlighter if you haven't yet.
Next, save blockescape.py (see below if the link ever dies) to
Preferences -> Browse Packages... -> BracketHighlighter/bh_modules/blockescape.py
Then, add this entry to the top of your user key bindings (Preferences -> Key Bindings — User):
{
"keys": ["tab"],
"command": "bh_key",
"args":
{
"lines" : true,
"plugin":
{
"type": ["__all__"],
"command": "bh_modules.blockescape"
}
}
},
I wouldn't recommend using tab as your trigger key, because tab has an important role already with expansions. Of course, you could define a special context in which to use tab, but that is up to you.
In case Github is ever down, here's the plugin code:
import bh_plugin
import sublime
DEFAULT_TAGS = ["cfml", "html", "angle"]
class BlockEscape(bh_plugin.BracketPluginCommand):
def run(self, edit, name, tags=DEFAULT_TAGS):
current_left, current_right = self.selection[0].begin(), self.selection[0].end()
left, right = self.left, self.right
first, last = left.end, right.begin
if left.end != right.end:
if name in tags and left.size() > 1:
first, last = right.begin + 1, right.begin + 1
if first == current_left and last == current_right:
first, last = right.end, right.end
else:
first, last = right.begin, right.begin
if first == current_left and last == current_right:
first, last = right.end, right.end
else:
# There is no second bracket, so just select the first
if name in tags and left.size() > 1:
first, last = left.begin + 1, left.begin + 1
else:
first, last = right.end, right.end
if first == current_left and last == current_right:
first, last = right.end, right.end
self.selection = [sublime.Region(first+1, last+1)]
def plugin():
return BlockEscape
Since I more or less hacked the plugin together, it might not work properly. In that case, feel free to edit it yourself or leave a comment on the Gist page.
You can add the $0 in a snippet to tab to that location:
<snippet>
<description>If Condition</description>
<content><![CDATA[if (${1:/* condition */}){
${2:/* code */}
}${0}]]></content>
<tabTrigger>if</tabTrigger>
<scope>source.c</scope>
</snippet>
Related
I am using following code to try to get GUI elements from a function:
mypanelfn: func[] [
collect[
repeat i 10 [
print append copy "i in loop: " i
keep [t: text] keep append copy "message: " i
keep [field "entry"
button "Click" [t/text: "clicked"] return]]]]
view [
do [mypanelfn]]
There are no error messages and loop go on all right and a windows is also displayed. But this is only a small empty windows without any text, fields or buttons.
What is wrong with this code?
Edit: putting probe before collect shows (I have added line breaks for clarity):
[t: text "message: 1" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 2" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 3" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 4" field "entry" button "Click" [t/text: "clicked"] return
t: text "message: 5" field "entry" button "Click" [t/text: "clicked"] return
This method does not require setting any variables—it works by containing each iteration of faces within a common parent (panel):
view collect [
keep [below space 0x0]
repeat i 10 [
keep compose/deep [
panel [
origin 0x0
text (rejoin ["Message Number: " i])
field "entry"
button "Click" [face/parent/pane/1/text: "clicked"]
]
]
]
]
face/parent is the panel face whose first child (pane/1) is the text box (origin does not create a face).
You don't necessarily need the function there, however:
view mypanelfn
Works.
Note: the equivalent code in Rebol requires layout: view layout mypanelfn
The reason this happens is because view processes blocks! (anything inside []). So you don't have to do it.
In general, it's better to think of Red as a functional, message passing language. Everything is an expression, in contrast to imperative languages with procedures and statements.
once again; you need unique names for elements you want to address. Here a solution with reduce instead of compose
mypanelfn: func[] [
collect[
repeat i 10 [
tname: to-word rejoin ['t i]
print append copy "i in loop: " i
keep reduce [to-set-word tname 'text] keep append copy "message: " i
keep reduce [
'field "entry" 'button "Click" reduce [
to-set-path reduce [
tname 'text ]
"clicked" ]
'return ] ] ] ]
I recommend that you use the commands in the console to see what they do. E.g.
rejoin ['t i] creates a string "t1"with t and the (reduced/get-)value of i.
to-word changes that to a Red(bol) word t1
to-setword tname creates a set-word t1:
to-set-path reduce [tname 'text ]creates a set-path t1/text:
this works for me in rebol/view:
lay: mypanelfn
insert head lay 'across
view layout lay
I think while you're learning this stuff you need to look at the generated VID code to check that there are no problems before trying to View it.
Making the *.sublime-completions file, I found the "bug" which occurs when typing a long function like:
one.two.three.four.five(One := One_var, Two := Two_Var);
So if you type the whole function:
"one.two.three.four.five" and then press tab - it will paste another part of functions without issues.
But, if you type "one.two.thr" - and then in pop-up hint see that function and hit "Enter" it will replaces like:
one.two.one.two.three.four.five(One := One_var, Two := Two_Var);
How can I configure trigger to replace "one.two.thr" to correct function format?
This is my trigger.
{ "trigger": "one.two.three.four.five", "contents": "${TM_CURRENT_LINE/(.*)/(one.two.three.four.five)/}(One := One_var, Two := Two_Var);" },
This is not a bug, but a default setting of Sublime Text. By default, dots are treated as word_separators. Hence every time you type ., the completion trigger will be reset.
Example:
Let's assume we have only the following completions available:
{
"trigger": "one.two.three",
"contents": "one.two.three.four.five"
},
{
"trigger": "two",
"contents": "it_takes_two"
}
Now, let's type them in Sublime Text (the | marks the cursor position!)
one| -> will trigger "one.two.three.four.five"
one.| -> new word, forgot about "one.two.three.four.five"
one.two| -> will trigger "it_takes_two"
In the last case, both completions will be listed in the completion popup does to the fuzzy match for ”two”. However, the closer match ”two” will have a higher priority.
To fix this, you can edit the word_separators setting in a package setting or your global user settings. By default the setting treats the following characters as word separators:
./\\()\"'-:,.;<>~!##$%^&*|+=[]{}`~?
Alternatively, you ommit the dots from your completion triggers, e.g. use one-two-three instead of one.two.three.
On Sublime Text 3 (but I guess it's the same with ST2), I know that when you copy ( CTRL + C ) when there is nothing selected, the entire line is copied but I need to know how to paste it below my cursor.
It currently paste it above it and it doesn't seem logical to me, is there a way to modify this behaviour ?
Following Dan Lowe's answer, I made this file :
http://pastebin.com/7nPWZCPh
and added this line
{ "keys": ["ctrl+shift+v"], "command": "run_macro_file", "args": {"file": "res://Packages/User/paste_no_line.sublime-macro"}},
to my user's keybindings.
Works as intended but I have two differente "paste command" now.
It's not that it pastes "above" or "below", it's that it is operating on the current line. When you copy without first making a selection, it copies the current line. When you paste that, it also operates on the current line -- it pastes the buffer into that line, and as a side effect, whatever was on that line is bumped out of the way to the next line. It can't be bumped upward instead - the file can only grow or add new lines downward, you can't grow upward beyond line 1.
As to how to modify the behavior, I would suggest trying to make a macro.
http://docs.sublimetext.info/en/latest/extensibility/macros.html
As you pointed out in the comments, a macro works but it leaves you with two different ways to do a paste, one for normal use and the other for this "entire line" behavior. That is unfortunate, though there is another (harder) solution. You could try to write a Sublime plugin to detect how to behave and do what you want in each case. This is a bit beyond my ability to do for you... but in thinking about this, I realized that the Vintage package already has a command for this, because its p and P keys paste before and after the cursor, respectively. I looked inside the Vintage package to find where they did it. Here is their code, though I couldn't explain to you exactly how it works. You would want to try to emulate ViPasteRight.
class ViPrefixableCommand(sublime_plugin.TextCommand):
# Ensure register and repeat are picked up from g_input_state, and that
# it'll be recorded on the undo stack
def run_(self, edit_token, args):
if not args:
args = {}
if g_input_state.register:
args['register'] = g_input_state.register
g_input_state.register = None
if g_input_state.prefix_repeat_digits:
args['repeat'] = digits_to_number(g_input_state.prefix_repeat_digits)
g_input_state.prefix_repeat_digits = []
if 'event' in args:
del args['event']
edit = self.view.begin_edit(edit_token, self.name(), args)
try:
return self.run(edit, **args)
finally:
self.view.end_edit(edit)
class ViPasteRight(ViPrefixableCommand):
def advance(self, pt):
if self.view.substr(pt) == '\n' or pt >= self.view.size():
return pt
else:
return pt + 1
def run(self, edit, register = '"', repeat = 1):
visual_mode = self.view.has_non_empty_selection_region()
if not visual_mode:
transform_selection(self.view, lambda pt: self.advance(pt))
self.view.run_command('paste_from_register', {'forward': not visual_mode,
'repeat': repeat,
'register': register})
class ViPasteLeft(ViPrefixableCommand):
def run(self, edit, register = '"', repeat = 1):
self.view.run_command('paste_from_register', {'forward': False,
'repeat': repeat,
'register': register})
And here is how they bind them to keys. If you wanted to try to adapt this you probably would not need the context, that is something they need due to Vintage mode's modal nature.
{ "keys": ["P"], "command": "vi_paste_left",
"context": [{"key": "setting.command_mode"}]
},
{ "keys": ["p"], "command": "vi_paste_right",
"context": [{"key": "setting.command_mode"}]
},
Here is the docs section about plugins, if you want to try to tackle it that way.
http://docs.sublimetext.info/en/latest/extensibility/plugins.html
I see no option to sort the sidebar in Sublime, how can I do it? I'd like to sort it alphabetically.
There is a Sublime plugin for that, called SortTabs. It works both with Sublime Text 2 and 3.
You can install it using Sublime Package Manger and then sort tabs using any of the following methods:
Sort Tabs by file name
Sort Tabs by file type
Sort Tabs by file path
Sort Tabs by modification date
Sort Tabs by last activation
I have found this answer
If you sort the tabs, it sorts the sidebar, and this sorts the tabs
I amended the classname though not to anything better. the classname was probably better before.
{ "keys": ["ctrl+alt+b"], "command": "sorttsortsidebar" }
http://www.sublimetext.com/forum/viewtopic.php?f=4&t=3876&start=20
import sublime_plugin
from os import path
from operator import itemgetter
# A simple command to sort current tabs alphabetically (returning focus to the
# original tab).
# Does not work with different groups or windows. Not catered for unsaved views
# (although it seems to work okay if there are any). It could be modified to
# work in these circumstances.
# { "keys": ["ctrl+alt+b"], "command": "sort_tabs" },
class SorttsortsidebarCommand(sublime_plugin.WindowCommand):
def run(self):
print("ddffd_sorttabs")
file_views = []
win = self.window
curr_view = win.active_view()
for vw in win.views():
_, tail = path.split(vw.file_name() or path.sep)
group, _ = win.get_view_index(vw)
file_views.append((tail.lower(), vw, group))
file_views.sort(key = itemgetter(2, 0))
moving_index = 0
for index, (_, vw, group) in enumerate(file_views):
if index == 0 or group > prev_group:
moving_index = 0
prev_group = group
else:
moving_index += 1
win.set_view_index(vw, group, moving_index)
win.focus_view(curr_view)
The quest
Turn this (with currHour being the current selection):
var currHour = now.getHours();
into this:
var currHour = now.getHours();
console.log('currHour=' + currHour);
Should work also for these cases:
currHour = now.getHours();
-->
currHour = now.getHours();
console.log('currHour=' + currHour);
and (where b is selected):
a = b;
-->
a = b;
console.log('b=' + b);
etc.
The Situation
Now, by following this answer I was able to produce the second line, with this:
<snippet>
<content>
<![CDATA[console.log('$SELECTION=' + $SELECTION);]]> </content>
<description>Print selection to console.log</description>
</snippet>
NB: my snippet code ommits <scope> because the scope is implied by the location of the snippet file (under language-specific dir -- JavaScript).
NB2: the <tabTrigger> setting in the snippet was ommited intentionally, because I will use a keyboard shortcut:
{ "keys": ["ctrl+shift+o"], "command": "insert_snippet", "args": { "name": "Packages/User/JavaScript/console-log-selection.sublime-snippet" } },
OK, that brings me half way there...
The problem
The current "solution" requires of me to manually duplicate the selection before I can invoke the snippet. That is an inconvenient step I'd like to skip if I can.
So, are there ways to make snippet duplicate the selection into new line?
I'm not great with snippets, so there may be another way. Anyways, you can do it with a plugin.
import re
import sublime
import sublime_plugin
class ConsoleLog(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
cursor = view.sel()[0]
line_region = view.line(cursor)
string = view.substr(line_region)
match = re.search(r"(\s*)(var\s+)?(\w+)\s*=", string)
if match:
if cursor.empty():
var_text = match.group(3)
else:
var_text = view.substr(cursor)
view.insert(edit, line_region.end(), "\n%sconsole.log('%s = ' + %s);" % (match.group(1), var_text, var_text))
end = view.line(line_region.end() + 1).end()
view.sel().clear()
view.sel().add(sublime.Region(end, end))
You just need to place the cursor on the line with your assignment operation. Bind your key binding to console_log. You can change the name as you see fit. Just be sure you change the name of the text command as well.
You could also modify your snippet so you don't have to type the text first. It seems unintuitive to type the variable name, select it, then hit your snippet key. Something like this may work better.
<snippet>
<content>
<![CDATA[console.log('$1 =' + $1);$2]]> </content>
<description>Print selection to console.log</description>
</snippet>
Edit:
I used the plugin to generate the console.logs with the cursor on the lines containing var foo = bar and foo = bar. That is, it's two invocations of the plugin command. Though you could modify it to handle multiple cursors also.
var foo = bar
console.log('foo = ' + foo)
foo = bar
console.log('foo = ' + foo)
I also modified the plugin so it should use a variable assignment automatically if nothing is selected. If something is selected, it will use the selected text in the console log. If you don't want the empty selection stuff, just remove the if statement under if match: and replace it with var_text = view.substr(cursor)
You can accomplish essentially what you want by combining a similar snippet with a macro.
The snippet:
<snippet>
<content><![CDATA[
${TM_CURRENT_LINE/var *(.+?) *=.+/\nconsole.log\('$1=' + $1\);/}
]]>
</content>
<description>Print selection to console.log</description>
</snippet>
The snippet figures out the variable name based on the current line and formats the console.log line using that variable.
The macro:
[
{"command": "move_to", "args": {"to": "hardeol", "extend": false}},
{"command": "insert_snippet", "args": {"name": "Packages/User/console-log-selection.sublime-snippet"}}
]
The macro uses an existing command - move_to - to get to the end of the line before inserting the snippet. This allows you to run the command from anywhere on the line.
Finally, the shortcut:
{ "keys": ["ctrl+shift+o"],
"command": "run_macro_file",
"args": { "file": "Packages/User/console-log-selection.sublime-macro" }
},
This seems like the best approach for inserting the log line if the variable assignment line already exists.
A macro will do what you want in both cases:
Save this in
~/Library/Application Support/Sublime Text 2/Packages/User/saran_macro.sublime-macro
[
{"command": "copy"},
{"command": "move_to", "args": {"to": "hardeol", "extend": false}},
{"command": "insert", "args": {"characters": "\nconsole.log('"}},
{"command": "paste"},
{"command": "insert", "args": {"characters": "=' + "}},
{"command": "paste"},
{"command": "insert", "args": {"characters":");"}}
]
Add this to your keybindings:
{ "keys": ["ctrl+shift+i"], "command": "run_macro_file", "args": { "file": "Packages/User/saran_macro.sublime-macro" } }
Whatever you highlight will be the variable in the macro.
Hello I Used the code in the selected answer to create this.
It supports multiple selections and put the correct identation. Also it dosen't have to be a "var foo =" line