How to choose different comment marks? - sublimetext3

In php we can use either # or // as line comments, and it is configured by default in the Comments.tmPreferences file as follows:
<dict>
<key>name</key>
<string>TM_COMMENT_START</string>
<key>value</key>
<string>// </string>
</dict>
<dict>
<key>name</key>
<string>TM_COMMENT_START_2</string>
<key>value</key>
<string># </string>
</dict>
I find the documents in https://docs.sublimetext.io/reference/comments.html, additional line comment marks can be used. But it didn't tell how to choose between them. My question is can I set up separate key bindings for each TM_COMMENT_START mark? And how?
This is the default key map setting:
{ "keys": ["ctrl+/"], "command": "toggle_comment", "args": { "block": false } }
which args should I use to choose different TM_COMMENT_START?

Looking at Packages/Default/comment.py, one can see that this is currently not possible without making some changes to the code, as it is hardcoded to always use the first block or line comment character defined in the tmPreferences file.
The following change can be made:
--- Shipped Packages/Default/comment.py 2018-10-11 19:11:54
+++ Packages/Default/comment.py 2018-10-22 10:54:03
## -168,7 +168,7 ##
for pos in start_positions:
view.insert(edit, pos, start)
- def add_comment(self, view, edit, comment_data, prefer_block, region):
+ def add_comment(self, view, edit, comment_data, prefer_block, region, preferred_index):
(line_comments, block_comments) = comment_data
if len(line_comments) == 0 and len(block_comments) == 0:
## -183,19 +183,19 ##
if region.empty():
if prefer_block:
# add the block comment
- self.block_comment_region(view, edit, block_comments[0], region)
+ self.block_comment_region(view, edit, block_comments[preferred_index], region)
else:
# comment out the line
- self.line_comment_region(view, edit, line_comments[0], region)
+ self.line_comment_region(view, edit, line_comments[preferred_index], region)
else:
if prefer_block:
# add the block comment
- self.block_comment_region(view, edit, block_comments[0], region)
+ self.block_comment_region(view, edit, block_comments[preferred_index], region)
else:
# add a line comment to each line
- self.line_comment_region(view, edit, line_comments[0], region)
-
- def run(self, edit, block=False):
+ self.line_comment_region(view, edit, line_comments[preferred_index], region)
+
+ def run(self, edit, block=False, preferred_index=0):
for region in self.view.sel():
comment_data = build_comment_data(self.view, region.begin())
if (region.end() != self.view.size() and
## -222,8 +222,8 ##
if self.remove_block_comment(self.view, edit, comment_data, line):
continue
- self.add_comment(self.view, edit, comment_data, block, line)
+ self.add_comment(self.view, edit, comment_data, block, line, preferred_index)
continue
# Add a comment instead
- self.add_comment(self.view, edit, comment_data, block, region)
+ self.add_comment(self.view, edit, comment_data, block, region, preferred_index)
and then one can modify the arguments sent from the keybinding to include the new preferred_index parameter set to 1 (to represent #) when working in a PHP context:
{ "keys": ["ctrl+/"], "command": "toggle_comment", "args": { "block": false, "preferred_index": 1 }, "context":
[
{ "key": "selector", "operator": "equal", "operand": "source.php", "match_all": true },
],
}

Related

Deleting until whitespace in Sublime Text

Is there any way to delete all characters untill first whitespace in Sublime. I know that you can use ctrl+delete to do that but it stops at non-word characters(",:,&,*, etc). When you try to delete aaa aaa 2+a, from the end, it will delete 2+a until + sign, but it will delete aaa until space. I need to change that so it will delete 2+a until first space. Solution can be anything; changing settings, plug-in.
I found solution for this. It's via this plugin:
https://packagecontrol.io/packages/KeyboardNavigation
Key for it is:
{ "keys": ["ctrl+backspace"], "command": "delete_to_beg_of_contig_boundary", "args": {"forward": false} }
It deletes any characters right to left until first whitespace.
I have written a Sublime Text plugin to delete text as you require. It is almost identical to ST's delete_word command but breaks only at whitespace/non-whitespace.
When called the plugin deletes text from the cursor to the next or previous group of characters, the grouping being defined as either whitespace or non-whitespace characters. Thus if run several times in succession it will alternate between deleting groups of whitespace and non-whitespace characters ahead or behind the cursor. The forwards parameter of the run() method (i.e. the command's arg) controls the deletion direction.
Save the plugin somewhere in your config Packages folder hierarchy. e.g.
.../sublime-text-3/Packages/User/DeleteToWhitespace.py
Add key bindings to your user .sublime-keymap file. e.g.
//
// These key bindings override the ST 'delete_word' keys but use whatever keys you want.
// You could use `super+delete` and `super+backspace` and keep ST's delete keys intact.
//
{ "keys": ["ctrl+delete"], "command": "delete_to_whitespace", "args": {"forwards": true} },
{ "keys": ["ctrl+backspace"], "command": "delete_to_whitespace", "args": {"forwards": false} },
Below is the DeleteToWhitespace.py plugin. It has been uploaded to this GitHub Gist – this links directly to the raw source code.
#
# Name: Delete To Whitespace
# Requires: Plugin for Sublime Text v3
# Command: delete_to_whitespace
# Args: forwards: bool (delete backwards if false)
# License: MIT License
#
import sublime, sublime_plugin, re
class DeleteToWhitespaceCommand(sublime_plugin.TextCommand):
"""
A Sublime Text plugin that deletes text from the cursor to the next or
previous group of characters, the grouping being defined as either
whitespace or non-whitespace characters. Thus if run several times in
succession it will alternate between deleting groups of whitespace and
non-whitespace ahead or behind the cursor. The forwards parameter of the
run() method (i.e. the command's arg) controls the deletion direction.
"""
def run(self, edit, forwards=True):
self.edit = edit
self.forwards = forwards
if forwards:
self.delete_forwards()
else:
self.delete_backwards()
def delete_forwards(self):
whitespace_regex = "^\s+"
non_whitespace_regex = "^\S+"
for sel in self.view.sel():
if sel.size() > 0:
self.view.erase(self.edit, sel)
continue
# ∴ sel.a == sel.b == sel.begin() == sel.end()
# view.full_line() includes the trailing newline (if any).
cursor = sel.a
line = self.view.full_line(cursor)
cursor_to_eol = sublime.Region(cursor, line.end())
cursor_to_eol_str = self.view.substr(cursor_to_eol)
match = re.search(whitespace_regex, cursor_to_eol_str)
if match:
self.erase_matching_characters(cursor, match)
continue
match = re.search(non_whitespace_regex, cursor_to_eol_str)
if match:
self.erase_matching_characters(cursor, match)
continue
def delete_backwards(self):
whitespace_regex = "\s+$"
non_whitespace_regex = "\S+$"
for sel in self.view.sel():
if sel.size() > 0:
self.view.erase(self.edit, sel)
continue
# ∴ sel.a == sel.b == sel.begin() == sel.end()
# view.line() excludes the trailing newline (if any).
cursor = sel.a
line = self.view.line(cursor)
cursor_to_bol = sublime.Region(cursor, line.begin())
cursor_to_bol_str = self.view.substr(cursor_to_bol)
# Delete the newline of the 'previous' line.
if cursor_to_bol.size() == 0 and cursor > 0:
erase_region = sublime.Region(cursor, cursor - 1)
self.view.erase(self.edit, erase_region)
continue
match = re.search(whitespace_regex, cursor_to_bol_str)
if match:
self.erase_matching_characters(cursor, match)
continue
match = re.search(non_whitespace_regex, cursor_to_bol_str)
if match:
self.erase_matching_characters(cursor, match)
continue
def erase_matching_characters(self, cursor, match):
match_len = match.end() - match.start()
if self.forwards:
erase_region = sublime.Region(cursor, cursor + match_len)
else:
erase_region = sublime.Region(cursor, cursor - match_len)
self.view.erase(self.edit, erase_region)

Split 30 Gb json file into smaller files

I am facing memory issue in reading a json file which is 30 GB in size. Is there any direct way in Python3.x like we have in unix where we can split the json file into smaller files based on the lines.
e.g. first 100000 records go in first slit file and then rest go to subsequent child json file?
Depending on your input data and if its structure is known and consistent, it will be more hard or easy.
In my example here the idea is to read the file line by line with a lazy generator and write new files whenever a valid object can be constructed from the input. Its a bit like manual parsing.
In the real world case this logic when to write to a new file would highly depend on your input and what you are trying to achieve.
Some sample data
[
{
"color": "red",
"value": "#f00"
},
{
"color": "green",
"value": "#0f0"
},
{
"color": "blue",
"value": "#00f"
},
{
"color": "cyan",
"value": "#0ff"
},
{
"color": "magenta",
"value": "#f0f"
},
{
"color": "yellow",
"value": "#ff0"
},
{
"color": "black",
"value": "#000"
}
]
# create a generator that yields each individual line
lines = (l for l in open('data.json'))
# o is used to accumulate some lines before
# writing to the files
o=''
# itemCount is used to count the number of valid json objects
itemCount=0
# read the file line by line to avoid memory issues
i=-1
while True:
try:
line = next(lines)
except StopIteration:
break
i=i+1
# ignore first square brackets
if i == 0:
continue
# in this data I know every 5th lines a new object will begin
# this logic depends on your input data
if i%4==0:
itemCount+=1
# at this point I am able to create avalid json object
# based on my knowledge of the input file structure
validObject=o+line.replace("},\n", '}\n')
o=''
# now write each object to its own file
with open(f'item-{itemCount}.json', 'w') as outfile:
outfile.write(validObject)
else:
o+=line
Here is a repl with the working example: https://replit.com/#bluebrown/linebyline

Sublime text: Separate comment/uncomment shortcuts

I know about toggling comments with Ctrl+/.
I'd prefer to be able to comment and uncomment lines by using different shortcuts. I.e.: Ctrl+r to comment and Ctrl+t to uncomment.
Anybody see a way to do that? I'm on sublime text 3.
You can do this by creating a new plugin:
from the Tools menu -> Developer -> New Plugin...
select all and replace with the following
import sublime
import sublime_plugin
from Default.comment import *
class AddOrRemoveCommentCommand(ToggleCommentCommand):
def run(self, edit, **kwargs):
block = kwargs.get('block', False)
for region in self.view.sel():
comment_data = build_comment_data(self.view, region.begin())
if (region.end() != self.view.size() and
build_comment_data(self.view, region.end()) != comment_data):
# region spans languages, nothing we can do
continue
if kwargs['mode'] in ('remove', 'toggle'):
if self.remove_block_comment(self.view, edit, comment_data, region):
continue
if self.is_entirely_line_commented(self.view, comment_data, region):
self.remove_line_comment(self.view, edit, comment_data, region)
continue
if kwargs['mode'] in ('add', 'toggle'):
has_line_comment = len(comment_data[0]) > 0
if not has_line_comment and not block and region.empty():
# Use block comments to comment out the line
line = self.view.line(region.a)
line = sublime.Region(
advance_to_first_non_white_space_on_line(self.view, line.a), line.b)
# Try and remove any existing block comment now
if kwargs['mode'] == 'toggle' and self.remove_block_comment(self.view, edit, comment_data, line):
continue
self.add_comment(self.view, edit, comment_data, block, line)
continue
# Add a comment instead
self.add_comment(self.view, edit, comment_data, block, region)
save it, in the folder ST recommends (Packages/User/) as something like add_or_remove_comment.py (file extension is important, the base name isn't)
create a 2 keybindings in your user keybindings file for the add_or_remove_comment comment command, with a mode argument set to add or remove as desired i.e.
{ "keys": ["ctrl+r"], "command": "add_or_remove_comment", "args": { "mode": "add" } },
{ "keys": ["ctrl+t"], "command": "add_or_remove_comment", "args": { "mode": "remove" } },
Note that Ctrl+R will override the default Goto Symbol binding, and Ctrl+T will override the default Transpose Text binding...

Is it possible in Sublime Text to navigate from keyboard to a file in search results? [duplicate]

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

Select disjoint chunks of code in Vim for yanking

I am wondering if I am able to do this in Vim:
Sample code:
require 'abstract_controller/collector'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/array/extract_options'
require 'IDONTWANTTHISLINETOBEINCLUDEDINMYYANKREGISTER'
require 'IDONTWANTTHISLINETOBEINCLUDEDINMYYANKREGISTER'
module ActionMailer #:nodoc:
class Collector
include AbstractController::Collector
attr_reader :responses
def initialize(context, &block)
#context = context
#responses = []
#default_render = block
end
def any(*args, &block)
options = args.extract_options!
raise "You have to supply at least one format" if args.empty?
args.each { |type| send(type, options.dup, &block) }
end
alias :all :any
def custom(mime, options={})
options.reverse_merge!(:content_type => mime.to_s)
#context.freeze_formats([mime.to_sym])
options[:body] = block_given? ? yield : #default_render.call
#responses << options
end
end
end
Now suppose I want to yank just some lines and put them in another file. Suppose I want to yank these block of lines:
Chunk 1:
require 'abstract_controller/collector'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/array/extract_options'
Chunk 2:
module ActionMailer #:nodoc:
class Collector
include AbstractController::Collector
attr_reader :responses
def initialize(context, &block)
#context = context
#responses = []
#default_render = block
end
Chunk 3:
def custom(mime, options={})
options.reverse_merge!(:content_type => mime.to_s)
#context.freeze_formats([mime.to_sym])
options[:body] = block_given? ? yield : #default_render.call
#responses << options
end
end
end
These lines don't form a continuous line group, they are separated. So to achieve what I want I have to yank these blocks in 3 steps, which I find quite annoying. Because I have to yank, switch buffer, put, switch buffer, yank, switch buffer, put... so on...
So, is there a way to do this more efficiently (in one step)?
Use a register in append mode:
Visually select first three lines, "ay
Visually select next 10 lines, "Ay (note the capital letter)
Visually select chunk 3, "Ay
Go to other buffer, "ap
You like registers? This answer is more in-depth.

Resources