Sublime Text: How to jump to file from Find Results using keyboard? - find

If you File > Find in Files... ⇧+⌘+F you're brought to the Find Results, listing the files and highlighted matches. You can double-click either the filename/path or the matched line to open the file at the right line.
I wonder if there is a way to do exactly what the double-click does via keyboard?
With Sublimes great file switching capabilities, I thought there must be a way to keep your hands on the keyboard when doing Find in Files....

Try Shift+F4 (fn+Shift+F4 on the Aluminum Keyboard).

It appears a plugin has been created to do this. Took a quick look, there are some additional features in the plugin. While my original answer below will work, it will be much easier to install an existing plugin.
https://sublime.wbond.net/packages/BetterFindBuffer
Doable with a plugin.
import sublime
import sublime_plugin
import re
import os
class FindInFilesGotoCommand(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
if view.name() == "Find Results":
line_no = self.get_line_no()
file_name = self.get_file()
if line_no is not None and file_name is not None:
file_loc = "%s:%s" % (file_name, line_no)
view.window().open_file(file_loc, sublime.ENCODED_POSITION)
elif file_name is not None:
view.window().open_file(file_name)
def get_line_no(self):
view = self.view
if len(view.sel()) == 1:
line_text = view.substr(view.line(view.sel()[0]))
match = re.match(r"\s*(\d+).+", line_text)
if match:
return match.group(1)
return None
def get_file(self):
view = self.view
if len(view.sel()) == 1:
line = view.line(view.sel()[0])
while line.begin() > 0:
line_text = view.substr(line)
match = re.match(r"(.+):$", line_text)
if match:
if os.path.exists(match.group(1)):
return match.group(1)
line = view.line(line.begin() - 1)
return None
Set up a key binding with the command find_in_files_goto. Be careful when doing this though. Ideally, there would be some setting that identifies this view as the "Find In Files" view, so you could use that as a context. But I'm not aware of one. Of course, if you do find one, let me know.
Edit
Pulling up the example key binding into the main body of the answer.
{
"keys": ["enter"],
"command": "find_in_files_goto",
"context": [{
"key": "selector",
"operator": "equal",
"operand": "text.find-in-files"
}]
}

on SublimeText 3 I had to use F4(for going to the current result file) and Shift +F4 (for previous result).
From the default keymap...
{ "keys": ["super+shift+f"], "command": "show_panel", "args": {"panel": "find_in_files"} },
{ "keys": ["f4"], "command": "next_result" },
{ "keys": ["shift+f4"], "command": "prev_result" },
I hope this post helps.
SP

the command 'next_result' will do this. using the neat idea muhqu posted about using scope, you can make it so that you can press 'enter' on the line that you want to goto:
,{ "keys": ["enter"], "command": "next_result", "context": [{"key": "selector",
"operator": "equal", "operand": "text.find-in-files" }]}

try Ctrl+P - this quick-opens files by name in your project, For a full list of keyboard shortcuts see here

It is possible to emulate a double click in Sublime Text by executing the drag_select command with an argument of "by": "words" (as seen in the Default sublime-mousemap file).
However, you need to pretend that the mouse is where the caret is for this work. The following plugin will do this (Tools menu -> Developer -> New Plugin..., and replace the template with the following):
import sublime
import sublime_plugin
class DoubleClickAtCaretCommand(sublime_plugin.TextCommand):
def run(self, edit, **kwargs):
view = self.view
window_offset = view.window_to_layout((0,0))
vectors = []
for sel in view.sel():
vector = view.text_to_layout(sel.begin())
vectors.append((vector[0] - window_offset[0], vector[1] - window_offset[1]))
for idx, vector in enumerate(vectors):
view.run_command('drag_select', { 'event': { 'button': 1, 'count': 2, 'x': vector[0], 'y': vector[1] }, 'by': 'words', 'additive': idx > 0 or kwargs.get('additive', False) })
To be used in combination with a keybinding like:
{ "keys": ["alt+/"], "command": "double_click_at_caret" },

Related

Change comment symbol location when using VSCode "Toggle Line Comment" command

Is it possible to customize the location of the comment symbol ('#' when using Python) in VSCode?
For example, if my code is:
def my_func():
value = 1
and I press CMD-/ on line 2, I get:
def my_func():
# value = 1
I would prefer to get:
def my_func():
# value = 1
Is there a way to modify the default behavior?
VSCode: 1.67.1
MacOS: 12.3.1
There is no built-in way to do that. You will need an extension. See https://stackoverflow.com/a/59448448/836330 for a previous answer using a different extension. Here is a better answer using an extension I made in the meantime, Find and Transform. But there are restrictions as noted below.
Make this keybinding (in your keybindings.json):
{
"key": "alt+/", // unfortunately, this cannot be ctrl+/
"command": "findInCurrentFile",
"args": {
{
"key": "alt+r",
"command": "findInCurrentFile",
"args": {
"preCommands": [
"cursorEnd",
"cursorHomeSelect",
"cursorHomeSelect"
],
"replace": "${LINE_COMMENT}${TM_CURRENT_LINE}",
"restrictFind": "line" // works on multiple lines, see the demo
},
"when": "editorLangId == python" // if you want to limit it to a language
},
}
You can use whatever keybinding you want, but not Ctrl+/ because then toggle off will not work.
Ctrl+/ will work to toggle off comments if you do not use it in the keybinding above to add comments.
Note: For this to work well you need to disable the following setting (which I have shown disabled for a particular language, python):
"[python]": {
"editor.comments.insertSpace": false
}
That goes into your settings.json.

Sublime Text 3 macro to delete x lines from caret

I'm wanting to create a Sublime Text 3 macro to select 111 lines from the cursor and delete them. I tried recording this, but all it did was delete one character backwards, which I'm still confused by. I then looked into writing the macro manually, and the relevant commands here, but I've still not been able to figure out from that page whether what I want is possible, and if it is, how. Would really appreciate some help with this.
There is no internal Sublime Text command that will do multiple moves at once, unfortunately. However it is possible to define your own movement command via plugin code, which allows you to work around that.
To go this route, select Tools > Developer > New Plugin... from the menu and replace the default code with the following, then save the file as a python file (e.g. move_repeat.py). By using the menu entry to do this, Sublime will ensure that your plugin is stored in your User package.
import sublime
import sublime_plugin
class MoveRepeatCommand(sublime_plugin.TextCommand):
def run(self, edit, repeat=1, **kwargs):
for num in range(repeat):
self.view.run_command("move", kwargs)
This implements a new command named move_repeat which is a drop in replacement for the move command, providing an extra argument of repeat that specifies how many times to perform the movement that you provide.
As a drop in replacement, this can be used for any cursor movement (lines, words, etc).
Using this, your macro becomes the following:
[
{
"command": "move_repeat",
"args":
{
"by": "lines",
"extend": true,
"forward": true,
"repeat": 111
}
},
{
"command": "left_delete"
}
]
Here I just created the macro by hand-coding it. If you wanted to do this with an actual macro recording you would need to go about that another way.
For example, you could record the macro going down only one line and deleting, then manually add the extra value after the fact, or you could bind a key binding to the repeated command first and then do a macro recording.
The Messy Way
I've since managed to figure out a way to record macros. For some reason, the recorder doesn't register text selection by mouse, so I had to select the text via keyboard shortcut while recording the macro. This does the job in that it creates the macro I describe, although it also results in a script that I'm guessing is far less elegant than it would be if written by someone in the know.
Here's a shortened version of the script I ended up with:
[
{
"args":
{
"by": "lines",
"extend": true,
"forward": true
},
"command": "move"
},
{
"args":
{
"by": "lines",
"extend": true,
"forward": true
},
"command": "move"
},
{
"args":
{
"by": "lines",
"extend": true,
"forward": true
},
"command": "move"
},
{
"args":
{
"by": "lines",
"extend": true,
"forward": true
},
"command": "move"
},
{
"args":
{
"by": "lines",
"extend": true,
"forward": true
},
"command": "move"
},
{
"args": null,
"command": "left_delete"
}
]
It's as crude as it gets. It works by using the move command to select one line at a time. This means that this command block needs to be repeated for each line that you want to select for deletion. In my case, I needed 111 of them. Hopefully someone will come along and offer a nicer solution, but until then, this does the job.

Using TM_SELECTED_TEXT in my custom snippets

With the November 2016 (version 1.8) release of VSCode Snippet Variables are now supported, specifically TM_SELECTED_TEXT.
This makes me happy as I have used these heavily in both Sublime Text and TextMate.
I can't figure out how to get it to work in VSCode. I've created the snippet they use as an example:
"in quotes": {
"prefix": "inq",
"body": "'${TM_SELECTED_TEXT:${1:type_here}}'"
}
I then enter some text, highlight it and that's where things start to break.
The idea is highlight some text, run the snippet and then ${TM_SELECTED_TEXT:${1:type_here}} is replaced with the highlighted text. The problem I'm having is that to run the snippet you need to type the prefix value (in this case inq) to run the snippet which over-writes your highlighted text which messes everything up.
In Sublime/Textmate I launched the snippet from a keyboard combination which left my text highlighted.
Is there a way, in VSCode, to either make this work as is or launch the snippet from a key combination like was available in Sublime?
Coming in 1.49 (it is in the Insiders' Build as of this edit) your example will finally work as you expected. See merged pull request.
Vscode will now "remember" your selected text if any, and when you type your snippet prefix, insert it into the TM_SELECTED_TEXT variable even though you seemingly over-typed that selected text.
As of v1.20 this has become easier as a new variable $CLIPBOARD has been added, see new snippet variables. So there is no need to assign and run a shortcut - but you must copy the selection to clipboard CTRL-C.
Your example could now be:
"in quotes": {
"prefix": "inq",
"body": "'$CLIPBOARD:${1:type_here}'"
}
Note: $CLIPBOARD works. There's no need for the extra curly brackets {$CLIPBOARD}.
With the word highlighted, press F1 and run the command "Insert Snippet", then select your snippet on the list.
Also you can edit your keybindings by going to File>Preferences>Keyboard Shortcuts and add some shortcut to the "editor.action.showSnippets" command, like this:
{
"key": "ctrl+alt+s",
"command": "editor.action.showSnippets",
"when": "editorTextFocus"
}
https://github.com/Microsoft/vscode/issues/17780
as per this thread you can assign exact snippet to keybinding by providing args.
keybinding example for bootstrap media queries
{
"key": "ctrl+alt+b",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"name": "bsup"
}
},
{
"key": "ctrl+alt+shift+b",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"name": "bsup_copy"
}
},
snippet example
"bsup": {
"prefix": "bsup",
"body": [
"#include media-breakpoint-up(md){",
"\t${TM_SELECTED_TEXT}",
"}"
],
"description": "Bootstrap media up"
},
"bsup_copy": {
"prefix": "bsup_copy",
"body": [
"${1:${TM_SELECTED_TEXT}}",
"#include media-breakpoint-up(md){",
"\t${2:${TM_SELECTED_TEXT}}",
"}"
],
"description": "Bootstrap media up + copy selected text"
},
UPD: moreover - you can define snippet directly in keybindings.json which seems to be even more convenient for me in some cases
{
"key": "cmd+shift+c",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "console.log('${TM_SELECTED_TEXT}', $TM_SELECTED_TEXT$1);"
}
}
using documentation from https://code.visualstudio.com/docs/editor/userdefinedsnippets i was able to customize snippets, i am using 'surround with' extension and can put my own snippet into settings.json as follows:
"html_h3-name": {
"label": "h3",
"description": "wrap by h3 with <a name=''>, top",
"snippet": "<h3><a name=\"${TM_SELECTED_TEXT/[\\s]/-/g}\"></a>$TM_SELECTED_TEXT\n\t<a class=\"small\" href=\"#top\">top</a>\n</h3>"
},
which takes highlighted code in VSCode and creates h3 header from it with a name link:
it converts 'aaa bbb ccc' to
<h3><a name="aaa-bbb-ccc"></a>aaa bbb ccc
<a class="small" href="#top">top</a>
</h3>

how to create sublime text macro with selected line of code?

I'm trying to do an other sublime text macro.
I will simplify what i would like :
I select a line of code, press the macro key and the macro will add 1 line of text above and 2 more below the selected line.
Ex:
My line of code
I select the line, press macro key and the code may be like :
echo('init');
My line of code
echo('After line');
echo('again after');
I already try the record macro tools but it's not working for the text selected and for go to the line after the selected.
I know how to use key binding it's just for the macro file...
Thanks for your help :)
You don't need a macro for that, you can directly create a keybinding (obviously you can also move it to a macro):
{
"keys": ["ctrl+alt+a"],
"command": "insert_snippet",
"args": {
"contents": "echo('init');\n$SELECTION\necho('After line');\necho('again after');"
},
"context":
[
{ "key": "selection_empty", "operator": "equal", "operand": false }
]
},
Aside: if you only write you macros to trigger them from keybindings you might be interested in the ChainOfCommand package.

Sublime Text autocomplete window closes when scrolling off the list

When scrolling the autocomplete list with up or down, if you go too far in either direction (e.g. there are no more suggestions), the list will close.
The behavior I want is to have the list wrap when reaching the end instead of close.
This is easy to fix with downward scrolling by assigning this hotkey:
{ "keys": ["down"], "command": "auto_complete", "context":
[ { "key": "auto_complete_visible" } ]
},
That's because the auto_complete command has built-in functionality to scroll downward each time it's invoked, which is why the hotkey works.
...But upward scrolling is different. I've tried about 20 different hotkey and macro combinations with no success.
I'm almost certain the only way to achieve this behavior is with a plugin, but unfortunately my Python skill level is nil.
If it matters, I'm manually invoking autocomplete with ctrl+space (the automatic popup is disabled).
I'm using Sublime Text 2.
Best solution: use auto_complete_cycle settings (added 26 March 2015):
Please use this new simple solution and not the python plugin
Sublime Text new version relased on 24 March 2015 has a new setting called auto_complete_cycle that implement this behaviour. Set it to true to iterate through the autocomplete results.
"auto_complete_cycle": true
Worst old solution: this custom plugin
I have just made this plugin that works well on Sublime Text 3 in Linux Mint. I have not tested it in Sublime Text 2 but think the plugin system it's the same, so, it should work on that version too. The workaround used it's not too pretty but works.
import sublime, sublime_plugin
class UpArrowInAutoCompleteCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.settings().set('autoCompleteFlag',True)
self.view.settings().set('initialPoint', self.view.sel()[0].begin())
""" Move one line up """
self.view.run_command('move', {"by": "lines", "forward": False});
""" Auto-complete was opened and up arrow was pressed, so if the cursor changes
(on_selection_modified will be triggered) we have gone outside the list.
If we were not in the first element on_selection_modified will not be triggered, so
we turn of the flag"""
sublime.set_timeout(lambda: self.view.settings().set('autoCompleteFlag', False),300)
class AutoCompleteSelectionModifiedTriggerCommand(sublime_plugin.EventListener):
def on_selection_modified(self, view):
if view.settings().get('autoCompleteFlag'):
""" If the up arrow was pressed and on_selection_modified
has been triggered, then we know that we were in the first element
of the list and we hitted the up arrow"""
view.settings().set('autoCompleteFlag', False)
initialPoint = view.settings().get('initialPoint')
""" We don't know how many words the auto_complete has, so,
in order to calculate that number, we move down in the list
till we get outside the list. After that we make the list appear
again and move down n-1 times to go (and stay) to the last line """
view.sel().clear()
view.sel().add(initialPoint)
view.run_command('auto_complete')
numLines = 0
while view.sel()[0].begin() == initialPoint:
view.run_command('move', {"by": "lines", "forward": True})
numLines += 1
if numLines == 401:
return
if numLines == 0:
return
view.sel().clear()
view.sel().add(initialPoint)
view.run_command('auto_complete')
numLine = 0
while numLine < (numLines-1):
view.run_command('move', {"by": "lines", "forward": True})
numLine += 1
To make the plugin use Tools>new Plugin and paste the code. Then save it in Packages/User folder. You can use Preferences>Browse Packages to find the Packages floder, inside which the User folder is located.
To make it work I added to my user key-bindings file this bindings (the second it's your own binding):
{
"keys": ["up"],
"command": "up_arrow_in_auto_complete",
"context": [{
"key": "auto_complete_visible",
"operator": "equal",
"operand": true
}]
}, {
"keys": ["down"],
"command": "auto_complete",
"context": [{
"key": "auto_complete_visible"
}]
}
Edit: this is an example result: