├── fzf-url.tmux ├── README.md ├── LICENSE.txt └── fzf-url.rb /fzf-url.tmux: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #=============================================================================== 3 | # Author: Wenxuan 4 | # Email: wenxuangm@gmail.com 5 | # Created: 2018-04-06 09:30 6 | #=============================================================================== 7 | SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 8 | 9 | conf() { 10 | local value 11 | value=$(tmux show -gqv "$1") 12 | [ -n "$value" ] && echo "$value" || echo "$2" 13 | } 14 | 15 | key="$(conf @fzf-url-bind u)" 16 | 17 | tmux bind-key "$key" run -b "$SCRIPT_DIR/fzf-url.rb"; 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tmux-fzf-url 2 | ============ 3 | 4 | Open URLs in the current pane with fzf. 5 | 6 | Prerequisites 7 | ------------- 8 | 9 | - [tmux][tmux] 3.3+ for popup support 10 | - [fzf][fzf] 0.53.0+ for `--tmux` option 11 | 12 | [tmux]: https://github.com/tmux/tmux 13 | [fzf]: https://github.com/junegunn/fzf 14 | 15 | Installation 16 | ------------ 17 | 18 | ### Using [TPM](https://github.com/tmux-plugins/tpm) 19 | 20 | Add this line to your tmux config file, then hit `prefix + I`: 21 | 22 | ```sh 23 | set -g @plugin 'junegunn/tmux-fzf-url' 24 | ``` 25 | 26 | Usage 27 | ----- 28 | 29 | Press `PREFIX` + `u`. 30 | 31 | Customization 32 | ------------- 33 | 34 | ```sh 35 | # Bind-key (default: 'u') 36 | set -g @fzf-url-bind 'u' 37 | ``` 38 | 39 | Acknowledgement 40 | --------------- 41 | 42 | This project is a fork of 43 | [wfxr/tmux-fzf-url](https://github.com/wfxr/tmux-fzf-url). However, most of 44 | the code was completely rewritten. 45 | 46 | [License](LICENSE.txt) 47 | ---------------------- 48 | 49 | ``` 50 | The MIT License (MIT) 51 | 52 | Copyright (c) 2021 Junegunn Choi 53 | Copyright (c) 2018 Wenxuan Zhang 54 | ``` 55 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Wenxuan Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /fzf-url.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'English' 5 | require 'shellwords' 6 | 7 | def executable(*commands) 8 | commands.find do |c| 9 | `command -v #{c.split.first}`.empty?.! 10 | end 11 | end 12 | 13 | def halt(message) 14 | system "tmux display-message #{Shellwords.escape(message)}" 15 | exit 16 | end 17 | 18 | def with(command) 19 | io = IO.popen(command, 'r+') 20 | begin 21 | stdout = $stdout 22 | $stdout = io 23 | begin 24 | yield 25 | rescue Errno::EPIPE 26 | nil 27 | end 28 | ensure 29 | $stdout = stdout 30 | end 31 | io.close_write 32 | io.readlines.map(&:chomp) 33 | end 34 | 35 | # TODO: Keep it simple for now 36 | def extract_urls(line) 37 | line.scan(%r{(?:https?|file)://[-a-zA-Z0-9@:%_+.~#?&/=]+[-a-zA-Z0-9@%_+.~#?&/=!]+}x) 38 | end 39 | 40 | lines = `tmux capture-pane -J -p -S -99999` 41 | urls = lines.each_line.map(&:strip).reject(&:empty?) 42 | .flat_map { |l| extract_urls(l) }.reverse.uniq 43 | halt 'No URLs found' if urls.empty? 44 | 45 | header = 'Press CTRL-Y to copy URL to clipboard' 46 | max_size = `tmux display-message -p "\#{client_width} \#{client_height}"`.split.map(&:to_i) 47 | size = [[*urls, header].map(&:length).max + 2 + 4 + 2, urls.length + 5 + 1 + 1].zip(max_size).map(&:min).join(',') 48 | opts = ['--tmux', size, '--multi', '--no-margin', '--no-padding', '--wrap', 49 | '--expect', 'ctrl-y', '--style', 'default', 50 | '--header', header, '--header-border', 'top', 51 | '--highlight-line', '--header-first', '--info', 'inline-right', 52 | '--padding', '1,1,0,1', 53 | '--border-label', ' URLs '].map(&:shellescape).join(' ') 54 | selected = with("fzf #{opts}") do 55 | puts urls 56 | end 57 | exit if selected.length < 2 58 | 59 | if selected.first == 'ctrl-y' 60 | # https://superuser.com/questions/288320/whats-like-osxs-pbcopy-for-linux 61 | copier = executable('reattach-to-user-namespace pbcopy', 62 | 'pbcopy', 63 | 'wl-copy', 64 | 'xsel --clipboard --input', 65 | 'xclip -selection clipboard') 66 | halt 'No command to control clipboard with' unless copier 67 | with(copier) do 68 | print selected.drop(1).join($RS).strip 69 | end 70 | halt 'Copied to clipboard' 71 | end 72 | 73 | opener = executable('open', 'xdg-open') 74 | opener = 'nohup xdg-open' if opener == 'xdg-open' 75 | halt 'No command to open URL with' unless opener 76 | selected.drop(1).each do |url| 77 | system "#{opener} #{Shellwords.escape(url)} &> /dev/null" 78 | end 79 | --------------------------------------------------------------------------------