Sublime Text autocomplete window closes when scrolling off the list - autocomplete

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:

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.

How to make VSCode cursor macro instantanious?

I'm using the multi-command extension, and after pasting some text in a macro, I move the cursor around a few times, like the following:
{
"command": "multiCommand.test",
"interval": 0,
"sequence":[
{
"command": "editor.action.insertSnippet",
"args": {"snippet": "some text\n\n"}
},
{"command" : "cursorUp"},
{"command" : "cursorUp"},
{"command" : "cursorEnd"},
]
}
Everything ends up correct, but there's a noticeable delay in the cursor executing these commands that I'd like to be instantaneous (e.g. i'd like to not see it go through every step but just end up at the right spot.) Is there some setting I can change, or other command I can use to just instruct the cursor to move to a certain coordinate in the text?
Add the field $0 in your snippet
"snippet": "some text$0\n\n"
And remove the cursor commands

Preserve spacing on indent or outdent with tab in VSCode

In VSCode, when I have:
/*
* Comment
*/
If I select it and hit tab, I get:
/*
* Comment
*/
If instead I had hit shift-tab, I get:
/*
* Comment
*/
Same happens with Ctrl-] and Ctrl-[ (if those are supposed to make a difference)
I hoped turning off autoIndent would stop this, but no dice. I also turned off C++ formatting in the JSON config:
{
"editor.autoIndent": false,
"editor.detectIndentation": false,
"C_Cpp.formatting": "Disabled"
}
There's an extension which shifts text by one character at a time which is a sort of proof-of-concept you could override your tab key with something like that. But it doesn't seem you should need an extension to disable this formatting.
Is editor.autoIndent: false supposed to do what I want, and just broken?
UPDATE: I have also raised this as an issue on the VSCode GitHub
If you set the Tab size to 1, it will do the same job as the extension you referenced.
You can set the Tab or Space size by clicking on the bottom-right corner:
Click on Spaces:4. Then, select Indent Using Spaces or Indent Using Tabs and choose the size 1.
UPDATE:
I found an approach that fully satisfies your requirement (though it's through an extension). After choosing a Tab/Space size of 1, install and load the multi-command extension to perform the 1-space indentation 'four' times. Then, go to your settings.json (File > Preferences > Settings) and add these two commands:
{
"macros": {
"tab4times": [
"tab",
"tab",
"tab",
"tab"
],
"shifttab4times": [
"outdent",
"outdent",
"outdent",
"outdent"
]
}
}
Then, in the keybindings.json file (CTRL+P and then type keybindings.json), modify the CTRL+] and CTRL+[ keys to execute the newly created commands:
[
{
"key": "ctrl+]",
"command": "macros.tab4times",
"when": "editorTextFocus && !editorReadonly"
},
{
"key": "ctrl+[",
"command": "macros.shifttab4times",
"when": "editorTextFocus && !editorReadonly"
}
]
After saving these configurations, go to your text. Now press the CTRL+] and CTRL+[ to see you desired behavior of indentation and outdentation, respectiovely.
Hope it helps.

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.

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

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" },