Is there a way to click a link in Firefox and open a file in an existing VIM session? - firefox

I know it's possible to open links in an html page (let's say, if you're using Firefox) with TextMate if the link has this format:
View
But is it possible to do a similar thing with VIM? Perhaps like so:
View
Ideally this would use an existing VIM session.
Cheers,
Bernie

Found a way to do it:
Add a Protocol handler to Firefox
Open firefox and navigate to about:config
Add the following keys
network.protocol-handler.warn-external.txmt boolean false
network.protocol-handler.external.txmt boolean true
#the last one is the path to the script we're about to create
network.protocol-handler.app.txmt string ~/protocol_handler/prot.sh
# I ended up needing this one as well on another machine, (no idea why)
network.protocol-handler.expose.txmt boolean false
Create the script ~/protocol_handler/prot.sh
Copy and paste the following into the file:
#! /usr/bin/env ruby
file_result = ARGV[0].scan(/file\:\/\/((\w|\/|\.)*).*/)
file_path = file_result[0][0]
line_result = ARGV[0].scan(/\&amp\;line\=(\d*).*/)
if line_result
line = line_result[0][0]
system "gvim --remote-silent +#{line} #{file_path}"
else
system "gvim --remote-silent #{file_path}"
end
Save the file.
Change the file mode to be executable:
$ chmod +x ~/protocol_handler/prot.sh
I'm not sure if you have to restart Firefox or not.
If you actually want to use the "vim://" protocol just change the ending on the network keys from txmt to vim. Since several Rails plugins (rails-footer, namely) out there already use txmt, I just used that to avoid recoding.
Have fun!
Berns

http://www.mozilla.org/projects/netlib/new-handler.html

To get tmxt:// links working with gedit, I had to use a bash script from #Rystraum's related answer instead of the Ruby, ~/bin/txmt_proto.bash:
#!/bin/bash
FILE=$1
FILE=$(echo $FILE | grep -o "file:/\/.\+" | cut -c 8- | sed -e 's/%2F/\//g')
LINE=$(echo $FILE | grep -o "\&line=[0-9]\+")
LINE=$(echo $LINE | grep -o "[0-9]\+")
FILE=$(echo $FILE | grep -o "\(.\+\)\&")
FILE=$(echo $FILE | cut -d'&' -f1)
gedit +$LINE $FILE
and change the Firefox config network.protocol-handler.app.txmt to point at the script:
network.protocol-handler.app.txmt string ~/bin/txmt_proto.bash

Related

multi process bash within fzf --preview feature

I am trying to use fzf in the following manner, I would like to be able to search for a term within my codebase and then with the preview window be able to see the file which contains the string I am searching for at the line where the string is found.
So far I have managed to fuzzy search through the codebase for various terms by piping a ripgrep search of all files in the directory and below. And I have used cut to parse out the file name for cat or tail to read and print to the preview window. This is the command used for that.
rg . -n | fzf --preview-"cut -d":" -f1 <<< {} | xargs cat"
Note the string represented by {} is in the following format:
myfile.c:72:The string I am fuzzy searching
My issue is that I cannot parse out both the filename and the line number.
I have tried passing a bashscript within the preview command as well as using $() in the following example. (Note that here I use tail with the --lines+N argument to print the file after line N)
rg . -n | fzf --preview-"tail $(cut -d":" -f1 <<< {}) --lines=+$(cut -d":" -f2 <<< {})"
This does not work nor does a variety of variants on this attempt. Any help or feedback is appreciated.
Edit(1) :
I've tried to split it into an array like so
rg . -n | fzf --preview="IFS=":" read -r -a arr <<< {}| xargs tail ${arr[0]} --lines=+${arr[1]}"
This works in that the preview does show the file at the line where the string is found however it does not update as I cycle through other fuzzy found suggestions.
So I eventually figured out a solution that works.
It involves calling a separate script from my subprocess running inside --preview. I used the following script to take the string which fzf passes to --preview (in the format of filename:linenumber:found_string) and then used bat to render a preview window with syntax highlighting.
This method is pretty good but is somewhat resource intensive. I'm hoping to lessen the load by adding to the ignore glob and using ripgrep rather then find as it seems that is more efficient.
The bashscript I call string2arg.sh
#!/bin/bash
string2arg() {
export arg_filename=$(cut -d":" -f1 <<< $1);
export arg_linenum=$(cut -d":" -f2 <<< $1);
min_offset=25
let max_offset="min_offset*3"
min=0
if (($min_offset < $arg_linenum)); then
let min="arg_linenum-$min_offset"
fi
let max="arg_linenum+$max_offset"
bat --color=always --highlight-line $arg_linenum --style=header,grid,numbers --line-range $min:$max $arg_filename;
}
This is then called from my fzf alias for searching as such:
alias fsearch='rg . -n -g "!*.html" | fzf --preview="source $SC/string2arg.sh; string2arg {}"'
where $SC is the path to my bashscript string2arg.sh.
If I'm searching for a term with the intent to open the file its found in at the line its found in I use the following bash alias.
alias vfsearch='export vfile=$(fsearch);vim +$(cut -d":" -f2 <<< $vfile) $(cut -d":" -f1 <<< $vfile)'
Also I happen to use the following defaults for
fzf and find them to work for me although since I've moved to tmux I find it sometimes better to show the preview window above rather then to the side.
export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--reverse --inline-info --ansi"
export FZF_COMPLETION_TRIGGER=']]'
I find this extremely useful and am planning on moving it inside of my vim sessions. Hope it helps others !
Screenshot to better illustrate the use case.

How do I open a file in VS Code terminal by partially matching the file name?

If I have a file named w5_align_example.cpp, how do I open that file in VS Code integrated terminal by only supplying the word align?
code w5_align_sample.cpp would open it but I sometimes only remember the keyword align unless I search in a separate command to see what the file begins with. I want to open in a single command instead.
I've tried:
$ ls | grep "align" | code which gives me Run with 'code -' to read output from another program (e.g. 'echo Hello World | code -'). error.
$ ls | grep "align" | code - opens up a new file called code-stdin-sfd.txt with the text w5_align_example.cpp inside.
What would be the simplest (i.e. shortest) command to do this?
ls | grep "align" | xargs -I{} code {}
or
code $(ls | grep "align")
You can just use *. It matches any string and can be used multiple times.
code *align*
In some shells, you can combine this with tab completion. Just type:
code *align*
And then press Tab. This will fill in the rest of the file name, but it will beep if there is more than one option.

Changing file extensions for all files in a directory on OS X

I have a directory full of files with one extension (.txt in this case) that I want to automatically convert to another extension (.md).
Is there an easy terminal one-liner I can use to convert all of the files in this directory to a different file extension?
Or do I need to write a script with a regular expression?
You could use something like this:
for old in *.txt; do mv $old `basename $old .txt`.md; done
Make a copy first!
Alternatively, you could install the ren (rename) utility
brew install ren
ren '*.txt' '#1.md'
If you want to rename files with prefix or suffix in file names
ren 'prefix_*.txt' 'prefix_#1.md'
Terminal is not necessary for this... Just highlight all of the files you want to rename. Right click and select "Rename ## items" and just type ".txt" into to the "Find:" box and ".md" into the "Replace with:" box.
The preferred Unix way to do this (yes, OS X is based on Unix) is:
ls | sed 's/^\(.*\)\.txt$/mv "\1.txt" "\1.md"/' | sh
Why looping with for if ls by design loops through the whole list of filenames? You've got pipes, use them. You can create/modify not only output using commands, but also commands (right, that is commands created by a command, which is what Brian Kernighan, one of the inventors of Unix, liked most on Unix), so let's take a look what the ls and the sed produces by removing the pipe to sh:
$ ls | sed 's/^\(.*\)\.txt$/mv "\1.txt" "\1.md"/'
mv "firstfile.txt" "firstfile.md"
mv "second file.txt" "second file.md"
$
As you can see, it is not only an one-liner, but a complete script, which furthermore works by creating another script as output. So let's just feed the script produced by the one-liner script to sh, which is the script interpreter of OS X. Of course it works even for filenames with spaces in it.
BTW: Every time you type something in Terminal you create a script, even if it is only a single command with one word like ls or date etc. Everything running in a Unix shell is always a script/program, which is just some ASCII-based stream (in this case an instruction stream opposed to a data stream).
To see the actual commands being executed by sh, just add an -x option after sh, which turns on debugging output in the shell, so you will see every mv command being executed with the actual arguments passed by the sed editor script (yeah, another script inside the script :-) ).
However, if you like complexity, you can even use awk and if you like to install other programs to just do basic work, there is ren. I know even people who would prefer to write a 50-lines or so perl script for this simple every-day task.
Maybe it's easier in finder to rename files, but if connected remotely to a Mac (e.g. via ssh), using finder is not possible at all. That's why cmd line still is very useful.
Based on the selected and most accurate answer above, here's a bash function for reusability:
function change_all_extensions() {
for old in *."$1"; do mv $old `basename $old ."$1"`."$2"; done
}
Usage:
$ change_all_extensions txt md
(I couldn't figure out how to get clean code formatting in a comment on that answer.)
No need to write a script for it just hit this command
find ./ -name "*.txt" | xargs -I '{}' basename '{}' | sed 's/\.txt//' | xargs -I '{}' mv '{}.txt' '{}.md'
You do not need a terminal for this one; here is a sample demonstration in MacOS Big Sur.
Select all the files, right-click and select "rename..."
Add the existing file extension in "Find" and the extension you want to replace with "Replace with".
And done!
I had a similar problem where files were named .gifx.gif at the end and this worked in OS X to remove the last .gif:
for old in *.gifx.gif; do
mv $(echo "$old") $(echo "$old" | sed 's/x.gif//');
done
cd $YOUR_DIR
ls *.txt > abc
mkdir target // say i want to move it to another directory target in this case
while read line
do
file=$(echo $line |awk -F. '{ print $1 }')
cp $line target/$file.md // depends if u want to move(mv) or copy(cp)
done < abc
list=ls
for file in $list
do
newf=echo $file|cut -f1 -d'.'
echo "The newf is $newf"
mv $file $newf.jpg
done

How to create a file using a variable as filename?

I'm testing mobile Android devices and I would like to redirect the device log on a file whose name indicates both the date and time of my test, and the device model that is being tested.
For the first issue, I have already resolved with
now=$(date +"%b_%d_%Y_%k_%M");adb logcat -c;adb logcat|tee $now
So:
$ echo $now
Jan_03_2012_13_09
and the tee command creates a file with this filename.
As for the device model I have written two bash lines that obtain it from adb shell, namely
device=$(adb shell cat /system/build.prop | grep "^ro.product.device=")
deviceshortname=$(echo $device | sed 's/ro.product.device=//g')
(not optimal as I am not very good in bash programming... :) but I manage to get
$ echo $deviceshortname
LT15i
My problem is how to combine $now and $deviceshortname to obtain a filename such as:
LT15i_Jan_03_2012_13_19
I tried to set another variable:
filename=($(echo $deviceshortname"_"$now))
and got:
$ echo $filename
LT15i_Jan_03_2012_13_19
but if I try redirecting the log:
$ adb logcat | tee $filename
I obtain such file:
-rw-r--r--+ 1 ele None 293 Jan 3 13:21 ?[01;31m?[K?[m?[KLT15i_Jan_03_2012_13_19
I don't know why these strange characters and what I'm doing wrong.
Something is adding color to your output. It might be grep(1), it might adb, it might be baked into the /system/build.prop resource that you're reading.
If you're lucky, it is being added by grep(1) -- because that is supremely easy to disable with --color=no:
device=$(adb shell cat /system/build.prop | grep --color=no "^ro.product.device=")
deviceshortname=$(echo $device | sed 's/ro.product.device=//g')
If the colors are being added by adb, then perhaps it has a command line option that asks it to avoid colorizing the output.
If the colors are hard-coded into the /sys/build.prop resource in some way, then you'll need some little tool that filters out the color codes. I don't have one handy (and it's bedtime) but you can probably build one starting with tr(1) to delete \033 ASCII ESC characters.
Looks like an ANSI sequence used by adb to color the output.
I'm not sure if I'm missing something, but this works for me
p1=foo
p2=$(date +%d_%m_%Y)
cat sample_file.txt | tee $p1"_"$p2
Just type: echo ${deviceshortname}${now} and it will do the trick.

Unix: How can I prepend output to a file?

Specifically, I'm using a combination of >> and tee in a custom alias to store new Homebrew updates in a text file, as well as output on screen:
alias bu="echo `date "+%Y-%m-%d at %H:%M"` \
>> ~/Documents/Homebrew\ Updates.txt && \
brew update | tee -a ~/Documents/Homebrew\ Updates.txt"
Question: What if I wish to prepend this output in my textfile, i.e. placed at the beginning of the file as opposed to appending it to the end?
Edit1: As someone reported in the answers below, the use of temp files might be a good approach, which at least helped me partially:
targetLog="~/Documents/Homebrew\ Updates.txt"
alias bu="(brew update | cat - $targetLog \
> /tmp/out1 && mv /tmp/out1 $targetLog \
&& echo `date "+%Y-%m-%d at %H:%M":%S` | \
cat - $targetLog > /tmp/out2 \
&& mv /tmp/out2 $targetLog)"
But the problem is the output to STDOUT (previously made possible by tee), which I'm not sure can be incorporated in this tempfile approach …?
sed will happily do that for you, using -i to edit in place, eg.
sed -i -e "1i `date "+%Y-%m-%d at %H:%M"`" some_file
This works by creating an output file:
Let's say we have the initial contents on file.txt
echo "first line" > file.txt
echo "second line" >> file.txt
So, file.txt is our 'bottom' text file. Now prepend into a new 'output' file
echo "add new first line" | cat - file.txt > output.txt # <--- Just this command
Now, output has the contents the way we want. If you need your old name:
mv output.txt file.txt
cat file.txt
The only simple and safe way to modify an input file using bash tools, is to use a temp file, eg. sed -i uses a temp file behind the scenes (but to be robust sed needs more).
Some of the methods used have a subtle "can break things" trap, when, rather than running your command on the real data file, you run it on a symbolic link (to the file you intend to modify). Unless catered for correctly, this can break the link and convert it into a real file which receives the mods and leaves the original real file without the intended mods and without the symlink (no error exit-code results)
To avoid this with sed, you need to use the --follow-symlinks option.
For other methods, just be aware that it needs to follow symlinks (when you act on such a link)
Using a temp file, then rm temp file works only if "file" is not a symlink.
One safe way is to use sponge from package moreutils
Unlike a shell redirect, sponge soaks up all its input before
opening
the output file. This allows for constructing pipelines that read from
and write to the same file.
sponge is a good general way to handle this type of situation.
Here is an example, using sponge
hbu=~/'Documents/Homebrew Updates.txt'
{ date "+%Y-%m-%d at %H:%M"; cat "$hbu"; } | sponge "$hbu"
Simplest way IMO would be to use echo and cat:
echo "Prepend" | cat - inputfile > outputfile
Or for your example basically replace the tee -a ~/Documents/Homebrew\ Updates.txt with cat - ~/Documents/Homebrew\ Updates.txt > ~/Documents/Homebrew\ Updates.txt
Edit: As stated by hasturkun this won't work, try:
echo "Prepend" | cat - file | tee file
But this isn't the most efficient way of doing it any more...
Similar to the accepted answer, however if you are coming here because you want to prepend to the first line - rather than prepend an entirely new line - then use this command.
sed -i "1 s/^/string_replacement/" some_file
The -i flag will do a replacement within the file (rather than creating a new file).
Then the 1 will only do the replacement on line 1.
Finally, the s command is used which has the following syntax s/find/replacement/flags.
In our case we don't need any flags. The ^ is called a caret and it is used to represent the very start of a string.
Try this http://www.unix.com/shell-programming-scripting/42200-add-text-beginning-file.html
There is no direct operator or command AFAIK.You use echo, cat, and mv to get the effect.
{ date; brew update |tee /dev/tty; cat updates.txt; } >updates.txt.new
mv updates.txt.new updates.txt
I've no idea why you want to do this. It's pretty standard that logs like this have later entries appearing, well, later in the file.

Resources