Sublime Text open all files containing search term - sublimetext2

When I pres ctrl+shift+F to search across all the files in the current scope, I get a new window listing all the files that contain that search term.
How can I quickly open all of these files?

Hold down the F4 key from the Search Results screen, and it will "Navigate to next match" - which causes it to open each file listed in the results.
Just a small note, if you are getting 10+ matches per file this method starts to fail because it gets slow.

Sublime doesn't have the ability to do this out of the box; however the plugin API gives you the power to create a plugin to do something like this fairly simply (depending on how you ultimately want it to work).
I assume there are plugins available for something like this, but for reference purposes here is a simple example:
import sublime
import sublime_plugin
class OpenAllFoundFilesCommand(sublime_plugin.TextCommand):
def run(self, edit, new_window=False):
# Collect all found filenames
positions = self.view.find_by_selector ("entity.name.filename.find-in-files")
if len(positions) > 0:
# Set up the window to open the files in
if new_window:
sublime.run_command ("new_window")
window = sublime.active_window ()
else:
window = self.view.window ()
# Open each file in the new window
for position in positions:
window.run_command ('open_file', {'file': self.view.substr (position)})
else:
self.view.window ().status_message ("No find results")
This provides a command named open_all_found_files which could be bound to a key, added to a menu, added to the command palette, etc.
Using the notion that sublime has a custom syntax for the find results with a scope dedicated to the matching filenames, this collects all such regions and then opens the associated files.
The optional command argument new_window can be passed and set to true to open the files in a new window; leaving it off or setting it to false opens the files in the same window as the find results. You can of course change the default as you see fit.

You can't do that from within Sublime Text.
If you are using Linux/UNIX/OSX you can open all the files that contain a particular string or matching regex by using a combination of grep and xargs on the command line using a command like this:
grep -rlZ "search_str_or_regex" /path/to/search/* | xargs -0 subl
// Command line options (may vary between OSes):
//
// grep -r Recurse directories
// grep -l Output only the filenames of the files which contain the search pattern
// grep -Z Output null terminated filenames
// xargs -0 Input filenames are null terminated
// xargs subl Sublime Text executable
//
// The combination of -Z and -0 allows filenames containing spaces to be handled
The files will be opened in the most recently used Sublime Text window. Add -n or --new-window after subl to have them opened in a new window.
If you are using Windows, consider using GOW or Cygwin.

I had to change the Code of OdatNurd. I had problems with the ":" and the " " at the end of the Filename... Under Mac OS Big Sur...
import sublime
import sublime_plugin
class OpenAllFoundFilesCommand(sublime_plugin.TextCommand):
"""
Collect the names of all files from a Find in Files result and open them
all at once, optionally in a new window.
"""
def run(self, edit, new_window=False):
# Collect all found filenames
positions = self.view.find_by_selector("entity.name.filename.find-in-files")
if len(positions) > 0:
# Set up the window to open the files in
if new_window:
sublime.run_command("new_window")
window = sublime.active_window()
else:
window = self.view.window()
# Open each file in the new window
for position in positions:
file = self.view.substr (position)
#print(file)
file = file.replace(":","")
window.run_command('open_file', {'file': file.strip()})
else:
self.view.window().status_message("No find results")
def is_enabled(self):
return self.view.match_selector(0, "text.find-in-files")

Related

Saving specific Excel sheet as .csv

I am trying figure out how to save a specific Excel sheet as CSV via command line on Linux.
I am able to save the first sheet with the command below:
libreoffice --headless --convert-to csv --outdir /tmp /tmp/test.xls
It seems that there should be a way to specify the sheet I want to save, but I am not able to find one.
Is there a way to save it via LibreOffice?
I know OP has probably moved on by now but since this was the first result in my search, I figured I'd take a stab at leaving an answer that works and is actually usable for the next googler.
First, LibreOffice still only lets you save the first sheet. If that is all you need, then try libreoffice --convert-to csv Test.ods. Interestingly, the GUI does the same thing - only letting you export the active sheet. So it's not that terminal is ignored so much that it is just a limitation in LibreOffice.
I needed to extract several sheets into separate csv files so "active sheet only" didn't cut it for me. After seeing this answer only had a macro as the suggestion, I kept looking. There were a few ways to get the other sheets in various places I found after this page. I don't recall any of them that allowed you to extract a specific sheet (unless it was some random github tool that I skipped over).
I liked the method of using the Gnumeric spreadsheet application because it is in most central repos and doesn't involve converting to xsl / xslx first. However, there are a few caveats to be aware of.
First, if you want to be able to extract only one sheet without knowing the sheet name ahead of time then this won't work. If you do know the sheet name ahead or time or are ok with extracting all the sheets, then this works fairly well. The sheet name can be used to create the output files so it's not completely lost which is nice too.
Second, if you want the quoting style to match the same style you'd get by manually exporting from the LibreOffice GUI, then you will need to forget the term "csv" and think in terms of "txt" until you finish the conversion (e.g. convert to .txt files then rename them). Otherwise, if you don't care about an exact match on quoting style, then this doesn't matter. I will show both ways below. If you don't know what a quoting style is, basically in csv if you have spaces or a string that contains , you put quotes around the cell value to distinguish from the commas used to separate text. Some programs quote everything, others quote if there are spaces and/or commas in the value, and others don't quote at all (or only quote for commas?).
Last, there seems to be a difference in the precision when converting via LibreOffice and Gnumeric's ssconvert tool. Not enough to matter for most people, for most use-cases. But still worth noting. In my original ods file, I had a formula that was taking the average of 3 cells with 58.14, 59.1, and 59.05 respectfully. This average came to 58.7633333333333 when I exported via the LibreOffice GUI. With ssconvert, the same value was 58.76333333333333 (e.g. it had one additional decimal place compared to LibreOffice version). I didn't really care for my purposes but if you need to exactly match LibreOffice or don't want the extra precision, then I guess it might matter.
From man ssconvert, we have the following options:
-S, --export-file-per-sheet: Export a file for each sheet if the exporter only supports one sheet at a time. The output filename is treated as a template in which sheet number is substituted for %n, sheet name is substituted for %s, and sheet object name is substituted for %o in case of graph export. If there are no substitutions, a default of ".%n" is added.
-O, --export-options=optionsstring : Specify parameters for the chosen exporter. optionsstring is a list of parameter=value pairs, separated by spaces. The parameter names and values allowed are specific to the exporter and are documented below. Multiple parameters can be specified
During my testing, the -O options were ignored if I specified the output file with a .csv extension. But if I used .txt then they worked fine.
I'm not covering them all and I'm paraphrasing so read the man page if you want more details. But some of the options you can provide in the optionsstring are as follows:
sheet: Name of the sheet. You can repeat this option for multiple sheets. In my testing, using indexes did NOT work.
separator: If you want a true comma separated values files, then we'll need to use commas.
format: I'll be using raw bc I want the unformatted values. If you need something special for dates, etc read the man page.
quoting-mode: when to quote values. can be always, auto, or never. If you want to mimic LibreOffice as closely as possible, choose never.
So let's get to a terminal.
# install gnomic on fedora
$ sudo dnf install -y gnumeric
# install gnomic on ubuntu/mint/debian
$ sudo apt-get install -y gnumeric
# use the ssconvert util from gnumeric to do the conversion
# let it do the default quoting - this will NOT match LibreOffice
# in this example, I am just exporting 1 named sheet using
# -S, --export-file-per-sheet
$ ssconvert -S -O 'sheet=mysheet2' Test.ods test_a_%s.csv
$ ls *.csv
test_a_mysheet2.csv
# same thing but more closely mimicking LibreOffice output
$ ssconvert -S -O 'sheet=mysheet2 separator=, format=raw quoting-mode=never' Test.ods test_b_%s.txt;
$ mv test_b_mysheet2.txt test_b_mysheet2.csv;
# Q: But what if I don't know the sheet names?
# A: then you'll need to export everything
# notice the 'sheet' option is removed from the
# list of -O options vs previous command
$ ssconvert -S -O 'separator=, format=raw quoting-mode=never' Test.ods test_c_%n_%s.txt;
$ ls test_c*
test_c_0_mysheet.txt test_c_3_yoursheet2.txt
test_c_1_mysheet2.txt test_c_4_yoresheet.txt
test_c_2_yoursheet.txt test_c_5_holysheet.txt
# Now to rename all those *.txt files to *.csv
$ prename 's/\.txt/\.csv/g' test_c_*.txt
$ ls test_c*
test_c_0_mysheet.csv test_c_3_yoursheet2.csv
test_c_1_mysheet2.csv test_c_4_yoresheet.csv
test_c_2_yoursheet.csv test_c_5_holysheet.csv
Command:
soffice --headless "macro:///Library1.Module1.ConvertSheet(~/Desktop/Software/OpenOffice/examples/input/Test1.ods, Sheet2)"
Code:
Sub ConvertSheet( SpreadSheetPath as String, SheetNameSeek as String)
REM IN SpreadSheetPath is the FULL PATH and file
REM IN SheetName sheet name to be found and converted to CSV
Dim Doc As Object
Dim Dummy()
SheetNameSeek=trim(SheetNameSeek)
If (Not GlobalScope.BasicLibraries.isLibraryLoaded("Tools")) Then
GlobalScope.BasicLibraries.LoadLibrary("Tools")
End If
REM content of an opened window can be replaced with the help of the frame parameter and SearchFlags:
SearchFlags = com.sun.star.frame.FrameSearchFlag.CREATE + _
com.sun.star.frame.FrameSearchFlag.ALL
REM Set up a propval object to store the filter properties
Dim Propval(1) as New com.sun.star.beans.PropertyValue
Propval(0).Name = "FilterName"
Propval(0).Value = "Text - txt - csv (StarCalc)"
Propval(1).Name = "FilterOptions"
Propval(1).Value = "44,34,76,1"
Url=ConvertToUrl(SpreadSheetPath)
Doc = StarDesktop.loadComponentFromURL(Url, "MyFrame", _SearchFlags, Dummy)
FileN=FileNameoutofPath(Url)
BaseFilename = Tools.Strings.GetFileNameWithoutExtension(FileN)
DirLoc=DirectoryNameoutofPath(ConvertFromUrl(Url),"/")+"/"
Sheets = Doc.Sheets
NumSheets = Sheets.Count - 1
For J = 0 to NumSheets
SheetName = Sheets(J).Name
if (SheetName = SheetNameSeek) then
Doc.getCurrentController.setActiveSheet(Sheets(J))
Filename = DirLoc + BaseFilename + "."+ SheetName + ".csv"
FileURL = convertToURL(Filename)
Doc.StoreAsURL(FileURL, Propval())
end if
Next J
Doc.close(true)
NextFile = Dir
End Sub
I ended up using xlsx2csv
Version 0.7.8 supports general xlsx files pretty well. It allows to specify the tab by number and by name.
It does not do a good job on macros and complication multi-sheet documents, but it does a very good job on regular multi-sheet xlsx documents.
Unfortunately, xlsx2csv does not support password protected xlsx, so for that I still have to use Win32::OLE Perl module and run it on Windows environment.
From what I can see Libreoffice still does not have the ability to select the tab via command line.

HG command line input receives unwanted default input automatically in THG

I've written an Hg hook (in Python) to check the validity of the committed files according to out team rules. One of these rules prohibits files larger than XX kB, unless agreed with the ream. In case a large file is committed, I would like the hook to ask the user to allow the file.
I implemented it like this:
import re, os, sys, mercurial
MAX_SIZE_KB = 500
def check_committed_files(ui, repo, **kwargs):
changelog = repo.changelog._cache
lines = changelog[2].splitlines()
ui.status("Checking files...\n")
for line in lines[3:-2]:
fn = line
ui.status(" " + fn)
# check file size
file_size_kb = float(os.stat(line).st_size) / 1024
if file_size_kb > MAX_SIZE_KB:
if ui.prompt(" Allow file [%s] of %g kB?" % (fn, file_size_kb)).lower() not in ['y', 'yes']:
ui.warn(" Not allowed by user\n")
return 1
ui.flush()
return 0
It all works well if I use Hg CLI. But when I use TortoiseHg, the prompt is automatically yes-ed, so I get this in console:
Allow file [test.txt] of 2573.49 kB? y
and the hook goes on. I would like TortoiseHg to show a dialogue with Yes/No buttons. Is it possible? I'd like to have the solution as portable as possible, so e.g. no external Python modules that users need to install.
Since this is my first attempt with Hg hooks, any other comments on my implementation are also much appreciated.

Is it possible to show the size of files next to the file names inside Sublime sidebar?

I run an application that generates and updates a number of files in a specific folder. While the application runs, I observe the content of the folder through the sublime sidebar. Because I am interested to see the current size of each file while the application runs, I have an open terminal (Mac) where I use the following command to get the live state of the folder.
watch -d ls -al -h folderName
I was wondering if I can obtain this information directly from sublime.
So my question is: Is it possible to have the size of each file next to the file-names in the sublime sidebar? And if yes, how?
Since the sidebar is not in the official API, I don't think this is possible or at least it is not easy.
However getting the information into sublime text is easy. You can archive this by using a view. Just execute the ls command and write the result in the view.
I wrote a small (ST3) plugin for this purpose:
import subprocess
import sublime
import sublime_plugin
# change to whatever command you want to execute
commands = ["ls", "-a", "-s", "-1", "-h"]
# the update interval
TIMEOUT = 2000 # ms
def watch_folder(view, watch_command):
"""create a closure to watch a folder and update the view content"""
window = view.window()
def watch():
# stop if the view is not longer open
open_views = [v.id() for v in window.views()]
if view.id() not in open_views:
print("closed")
return
# execute the command and read the output
output = subprocess.check_output(watch_command).decode()
# replace the view content with the output
view.set_read_only(False)
view.run_command("select_all")
view.run_command("insert", {"characters": output})
view.set_read_only(True)
# call this function again after the interval
sublime.set_timeout(watch, TIMEOUT)
return watch
class WatchFolderCommand(sublime_plugin.WindowCommand):
def run(self):
folders = self.window.folders()
if not folders:
sublime.error_message("You don't have opened any folders")
return
folder = folders[0] # get the first folder
watch_command = commands + [folder]
# create a view and set the desired properties
view = self.window.new_file()
view.set_name("Watch files")
view.set_scratch(True)
view.set_read_only(True)
view.settings().set("auto_indent", False)
# create and call the watch closure
watch_folder(view, watch_command)()
Just open the User folder (or any other sub-folder of Packages), create a python file (e.g. watch_folder.py) and paste the source code.
You can bind it to a keybinding by pasting the following to your keymap:
{
"keys": ["ctrl+alt+shift+w"],
"command": "watch_folder",
},

ROT47 of the current selection in SublimeText

When doing CTRL+SHIFT+P, there is command named Rot13 selection which allows to encrpyt the selected text.
I'd like to add a command named Rot47 selection that does:
selection = 'Test'
print ''.join(chr(33 + ((ord(ch) + 14) % 94)) for ch in selection)
#Output: %6DE
Where to write this Python code in SublimeText to have this new command present in CTRL+SHIFT+P?
You can write a plugin.
Some beginner (and not 100% correct) code is available here.
A complete plugin for Rot47 would have the following code:
import sublime, sublime_plugin
class Rot47Command(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
if not region.empty():
s = self.view.substr(region)
s = ''.join(chr(33 + ((ord(ch) + 14) % 94)) for ch in s)
self.view.replace(edit, region, s)
Where to write this code?
Tools > New Plugin... will open a new buffer with some boilerplate code. Replace the boilerplate with the above code and save the file as rot47.py in /<sublime-text-dir>/Packages/User/.
You can test the above plugin by opening the console using Ctrl+`, and typing view.run_command('rot47') and hitting Enter. Make sure that you've selected some text before running your new Rot47 command.
Furthermore, if you want to create a keyboard shortcut for your new rot47 command: go to Preferences > Key Bindings -- User and add the following entry:
{ "keys": ["ctrl+shift+4"], "command": "rot47" }
(you can of course choose a more meaningful key combination.)
What did the above plugin code do?
self.view.sel() gives an iterable on the selected text regions (there can be multiple selections in the same buffer, go Sublime!). A region is basically a (start_index, end_index) pair that denotes a selected substring. self.view.substr(region) gives you the required substring.
We then modify the selection text (variable s) as desired and replace the selection with the new text (the call to self.view.replace()).
For a more extensive API reference, see this.

Insert file (foo.txt) into open file (bar.txt) at caret position

What would be the best method, please, to insert file (foo.txt) into open file (bar.txt) at caret position?
It would be nice to have an open-file dialog to choose anything to be inserted.
The word processing equivalent would be "insert file" here.
Here is a substitute for foo.sublime-snippet, which can be linked to form files elsewhere:
import sublime, sublime_plugin
class InsertFileCommand(sublime_plugin.TextCommand):
def run(self, edit):
v = self.view
template = open('foo.txt').read()
print template
v.run_command("insert_snippet", {"contents": template})
From within a text command you can access the current view. You can get the cursor positions using self.view.sel(). I don't know how to do gui stuff in python, but you can do file selection using the quick panel (similar to FuzzyFileNav).
Here is my unofficial modification of https://github.com/mneuhaus/SublimeFileTemplates which permits me to insert-a-file-here using the quick panel. It works on an OSX operating system (running Mountain Lion).
The only disadvantage I see so far is the inability to translate a double-slash \\ in the form file correctly -- it gets inserted instead as just a single-slash \. In my LaTex form files, the double-slash \\ represents a line ending, or a new line if preceded by a ~. The workaround is to insert an extra slash at each occurrence in the actual form file (i.e., put three slashes, with the understanding that only two slashes will be inserted when running the plugin). The form files need to be LF endings and I'm using UTF-8 encoding -- CR endings are not translated properly. With a slight modification, it is also possible to have multiple form file directories and/or file types.
import sublime, sublime_plugin
import os
class InsertFileCommand(sublime_plugin.WindowCommand):
def run(self):
self.find_templates()
self.window.show_quick_panel(self.templates, self.template_selected)
def find_templates(self):
self.templates = []
self.template_paths = []
for root, dirnames, filenames in os.walk('/path_to_forms_directory'):
for filename in filenames:
if filename.endswith(".tex"): # extension of form files
self.template_paths.append(os.path.join(root, filename))
self.templates.append(os.path.basename(root) + ": " + os.path.splitext(filename)[0])
def template_selected(self, selected_index):
if selected_index != -1:
self.template_path = self.template_paths[selected_index]
print "\n" * 25
print "----------------------------------------------------------------------------------------\n"
print ("Inserting File: " + self.template_path + "\n")
print "----------------------------------------------------------------------------------------\n"
template = open(self.template_path).read()
print template
view = self.window.run_command("insert_snippet", {'contents': template})
sublime.status_message("Inserted File: %s" % self.template_path)