diff --git a/.config/dunst/dunstrc b/.config/dunst/dunstrc new file mode 100644 index 0000000..5915d74 --- /dev/null +++ b/.config/dunst/dunstrc @@ -0,0 +1,248 @@ +[global] + font = Mono 16 + + # Allow a small subset of html markup: + # bold + # italic + # strikethrough + # underline + # + # For a complete reference see + # . + # If markup is not allowed, those tags will be stripped out of the + # message. + allow_markup = yes + + # The format of the message. Possible variables are: + # %a appname + # %s summary + # %b body + # %i iconname (including its path) + # %I iconname (without its path) + # %p progress value if set ([ 0%] to [100%]) or nothing + # Markup is allowed + format = "%s\n%b" + + # Sort messages by urgency. + sort = no #yes + + # Show how many messages are currently hidden (because of geometry). + indicate_hidden = no + + # Alignment of message text. + # Possible values are "left", "center" and "right". + alignment = left + + # The frequency with which text that is longer than the notification + # window allows bounces back and forth. + # This option conflicts with "word_wrap". + # Set to 0 to disable. + bounce_freq = 0 + + # Show age of message if message is older than show_age_threshold + # seconds. + # Set to -1 to disable. + show_age_threshold = -1 #60 + + # Split notifications into multiple lines if they don't fit into + # geometry. + word_wrap = yes + + # Ignore newlines '\n' in notifications. + ignore_newline = no + + + # The geometry of the window: + # [{width}]x{height}[+/-{x}+/-{y}] + # The geometry of the message window. + # The height is measured in number of notifications everything else + # in pixels. If the width is omitted but the height is given + # ("-geometry x2"), the message window expands over the whole screen + # (dmenu-like). If width is 0, the window expands to the longest + # message displayed. A positive x is measured from the left, a + # negative from the right side of the screen. Y is measured from + # the top and down respectively. + # The width can be negative. In this case the actual width is the + # screen width minus the width defined in within the geometry option. + geometry = "400x10-40+30" + + # Shrink window if it's smaller than the width. Will be ignored if + # width is 0. + shrink = no + + # The transparency of the window. Range: [0; 100]. + # This option will only work if a compositing window manager is + # present (e.g. xcompmgr, compiz, etc.). + transparency = 0 + + # Don't remove messages, if the user is idle (no mouse or keyboard input) + # for longer than idle_threshold seconds. + # Set to 0 to disable. + idle_threshold = 120 + + # Which monitor should the notifications be displayed on. + monitor = 0 + + # Display notification on focused monitor. Possible modes are: + # mouse: follow mouse pointer + # keyboard: follow window with keyboard focus + # none: don't follow anything + # + # "keyboard" needs a window manager that exports the + # _NET_ACTIVE_WINDOW property. + # This should be the case for almost all modern window managers. + # + # If this option is set to mouse or keyboard, the monitor option + # will be ignored. + follow = mouse + + # Should a notification popped up from history be sticky or timeout + # as if it would normally do. + sticky_history = no # yes + + # Maximum amount of notifications kept in history + history_length = 20 + + # Display indicators for URLs (U) and actions (A). + show_indicators = no + + # The height of a single line. If the height is smaller than the + # font height, it will get raised to the font height. + # This adds empty space above and under the text. + line_height = 0 + + # Draw a line of "separator_height" pixel height between two + # notifications. + # Set to 0 to disable. + separator_height = 2 + + # Padding between text and separator. + padding = 8 + + # Horizontal padding. + horizontal_padding = 8 + + # Define a color for the separator. + # possible values are: + # * auto: dunst tries to find a color fitting to the background; + # * foreground: use the same color as the foreground; + # * frame: use the same color as the frame; + # * anything else will be interpreted as a X color. + separator_color = frame + + # Print a notification on startup. + # This is mainly for error detection, since dbus (re-)starts dunst + # automatically after a crash. + startup_notification = false + + # dmenu path. + dmenu = /usr/bin/dmenu -p dunst: + + # Browser for opening urls in context menu. + browser = /usr/bin/firefox -new-tab + + # Align icons left/right/off + icon_position = off + + # Paths to default icons. + icon_folders = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/ + +[frame] + width = 1 + color = "#424242" + +[shortcuts] + + # Shortcuts are specified as [modifier+][modifier+]...key + # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2", + # "mod3" and "mod4" (windows-key). + # Xev might be helpful to find names for keys. + + # Close notification. + close = ctrl+space + + # Close all notifications. + close_all = ctrl+shift+space + + # Redisplay last message(s). + # On the US keyboard layout "grave" is normally above TAB and left + # of "1". + history = ctrl+grave + + # Context menu. + context = ctrl+shift+period + +[urgency_low] + # IMPORTANT: colors have to be defined in quotation marks. + # Otherwise the "#" and following would be interpreted as a comment. + background = "#222222" + #foreground = ${xrdb:color6} + foreground = "#dfdfdf" + timeout = 5 + +[urgency_normal] + background = "#222222" + #foreground = "${xrdb:color6} + foreground = "#dfdfdf" + timeout = 5 + +[urgency_critical] + background = "#222222" + #foreground = ${xrdb:color6} + foreground = "#dfdfdf" + timeout = 5 + +# Every section that isn't one of the above is interpreted as a rules to +# override settings for certain messages. +# Messages can be matched by "appname", "summary", "body", "icon", "category", +# "msg_urgency" and you can override the "timeout", "urgency", "foreground", +# "background", "new_icon" and "format". +# Shell-like globbing will get expanded. +# +# SCRIPTING +# You can specify a script that gets run when the rule matches by +# setting the "script" option. +# The script will be called as follows: +# script appname summary body icon urgency +# where urgency can be "LOW", "NORMAL" or "CRITICAL". +# +# NOTE: if you don't want a notification to be displayed, set the format +# to "". +# NOTE: It might be helpful to run dunst -print in a terminal in order +# to find fitting options for rules. + +#[espeak] +# summary = "*" +# script = dunst_espeak.sh + +#[script-test] +# summary = "*script*" +# script = dunst_test.sh + +#[ignore] +# # This notification will not be displayed +# summary = "foobar" +# format = "" + +#[signed_on] +# appname = Pidgin +# summary = "*signed on*" +# urgency = low +# +#[signed_off] +# appname = Pidgin +# summary = *signed off* +# urgency = low +# +#[says] +# appname = Pidgin +# summary = *says* +# urgency = critical +# +#[twitter] +# appname = Pidgin +# summary = *twitter.com* +# urgency = normal +# +# vim: ft=cfg + diff --git a/.config/iptables b/.config/iptables new file mode 100755 index 0000000..930c637 --- /dev/null +++ b/.config/iptables @@ -0,0 +1,37 @@ +# CONFIGURATION: +# Accept on localhost +iptables -A INPUT -i lo -j ACCEPT +iptables -A OUTPUT -o lo -j ACCEPT + +# Accept outgoing network +iptables -A OUTPUT -o eth0 -j ACCEPT + +# Allow established sessions to receive traffic +iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +iptables -A INPUT -m state --state NEW,ESTABLISHED -j ACCEPT + +# Enable SSH (to internet) +#iptables -A OUTPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT +#iptables -A OUTPUT -p tcp -m state --state NEW -m tcp --dport 10571 -j ACCEPT + +# Disable SSH (to that machine) +iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --sport 22 -j DROP +iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 22 -j DROP + +# Enable INZ +#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 1285 -j ACCEPT +#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 3000 -j ACCEPT +#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 8080 -j ACCEPT + +#Enable CSGO +iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27015 -j ACCEPT +iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27020 -j ACCEPT +iptables -A INPUT -p udp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27015 -j ACCEPT +iptables -A INPUT -p udp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27020 -j ACCEPT + +iptables -A INPUT -i eth0 -m state --state NEW,INVALID -j DROP +iptables -A FORWARD -i eth0 -m state --state NEW,INVALID -j DROP +# END OF CONFIG + +# END CONFIGURATION: +# END OF CONFIGURATION diff --git a/.config/lutris/lutris.conf b/.config/lutris/lutris.conf new file mode 100644 index 0000000..072c3f1 --- /dev/null +++ b/.config/lutris/lutris.conf @@ -0,0 +1,8 @@ +[lutris] +library_ignores = +migration_version = 10 +width = 1908 +height = 1001 +maximized = False +selected_category = runner:linux + diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf new file mode 100644 index 0000000..6d42a61 --- /dev/null +++ b/.config/mpv/mpv.conf @@ -0,0 +1,17 @@ +# Always allow seeking, e.g. allow seeking within a local cache of HTTP stream +force-seekable=yes +# Always open a video window even with no video +force-window=yes +# Don't exit when the end of playlist is reached +keep-open=yes +# Always save the current playback position on exit +save-position-on-quit=yes + +# Create 'high-quality' profile +[high-quality] +# Describe this profile +profile-desc="High quality rendering" +# Include all settings from the default 'opengl-hq' profile +profile=opengl-hq +# Disable debanding for better performance +deband=no diff --git a/.config/mpv/scripts/iptv.lua b/.config/mpv/scripts/iptv.lua new file mode 100644 index 0000000..ab43a6d --- /dev/null +++ b/.config/mpv/scripts/iptv.lua @@ -0,0 +1,505 @@ +--redefine keybindings here if needed; multiple bindings are possible +keybinds = { + activate = {'\\', 'MOUSE_BTN2'}, + plsup = {'UP', 'MOUSE_BTN3'}, + plsdown = {'DOWN', 'MOUSE_BTN4'}, + plsenter = {'ENTER', 'MOUSE_BTN0'} + } +--hide playlist after specified number of seconds +osd_time=10 +--show only specified number of playlist entries +window=7 +--fade video when showing playlist +fade=false +--if fade=true; -100 — black, 0 — normal +plsbrightness=-70 +--favorites get promotion to the top of the pls +favorites = {} +-- END OF CONFIGURABLE VARIABLES + +-- put your settings in (SCRIPTS DIR)/_iptvconf.lua +pcall(require, "_iptvconf") + +local timer +--local plscount +local pattern="" +local is_active +local is_playlist_loaded + +-- UTF-8 lower/upper conversion +local utf8_lc_uc = { + ["a"] = "A", + ["b"] = "B", + ["c"] = "C", + ["d"] = "D", + ["e"] = "E", + ["f"] = "F", + ["g"] = "G", + ["h"] = "H", + ["i"] = "I", + ["j"] = "J", + ["k"] = "K", + ["l"] = "L", + ["m"] = "M", + ["n"] = "N", + ["o"] = "O", + ["p"] = "P", + ["q"] = "Q", + ["r"] = "R", + ["s"] = "S", + ["t"] = "T", + ["u"] = "U", + ["v"] = "V", + ["w"] = "W", + ["x"] = "X", + ["y"] = "Y", + ["z"] = "Z", + ["а"] = "А", + ["б"] = "Б", + ["в"] = "В", + ["г"] = "Г", + ["д"] = "Д", + ["е"] = "Е", + ["ж"] = "Ж", + ["з"] = "З", + ["и"] = "И", + ["й"] = "Й", + ["к"] = "К", + ["л"] = "Л", + ["м"] = "М", + ["н"] = "Н", + ["о"] = "О", + ["п"] = "П", + ["р"] = "Р", + ["с"] = "С", + ["т"] = "Т", + ["у"] = "У", + ["ф"] = "Ф", + ["х"] = "Х", + ["ц"] = "Ц", + ["ч"] = "Ч", + ["ш"] = "Ш", + ["щ"] = "Щ", + ["ъ"] = "Ъ", + ["ы"] = "Ы", + ["ь"] = "Ь", + ["э"] = "Э", + ["ю"] = "Ю", + ["я"] = "Я", + ["ё"] = "Ё" +} + +local utf8_uc_lc = { + ["A"] = "a", + ["B"] = "b", + ["C"] = "c", + ["D"] = "d", + ["E"] = "e", + ["F"] = "f", + ["G"] = "g", + ["H"] = "h", + ["I"] = "i", + ["J"] = "j", + ["K"] = "k", + ["L"] = "l", + ["M"] = "m", + ["N"] = "n", + ["O"] = "o", + ["P"] = "p", + ["Q"] = "q", + ["R"] = "r", + ["S"] = "s", + ["T"] = "t", + ["U"] = "u", + ["V"] = "v", + ["W"] = "w", + ["X"] = "x", + ["Y"] = "y", + ["Z"] = "z", + ["А"] = "а", + ["Б"] = "б", + ["В"] = "в", + ["Г"] = "г", + ["Д"] = "д", + ["Е"] = "е", + ["Ж"] = "ж", + ["З"] = "з", + ["И"] = "и", + ["Й"] = "й", + ["К"] = "к", + ["Л"] = "л", + ["М"] = "м", + ["Н"] = "н", + ["О"] = "о", + ["П"] = "п", + ["Р"] = "р", + ["С"] = "с", + ["Т"] = "т", + ["У"] = "у", + ["Ф"] = "ф", + ["Х"] = "х", + ["Ц"] = "ц", + ["Ч"] = "ч", + ["Ш"] = "ш", + ["Щ"] = "щ", + ["Ъ"] = "ъ", + ["Ы"] = "ы", + ["Ь"] = "ь", + ["Э"] = "э", + ["Ю"] = "ю", + ["Я"] = "я", + ["Ё"] = "ё" +} + +--utf8 char pattern +local utf8_char="[\1-\127\192-\223][\128-\191]*" + +local cyr_chars={'а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я'} + +-- символы, которые возможно вводить для поиска +local chars={} +for i=string.byte('a'),string.byte('z') do + table.insert(chars,i) +end +for i=string.byte('A'),string.byte('Z') do + table.insert(chars,i) +end +for i=string.byte('0'),string.byte('9') do + table.insert(chars,i) +end +for _,v in ipairs({',','^','$','(',')','%','.','[',']','*','+','-','?','`',"'",";"}) do + table.insert(chars,string.byte(v)) +end + +local keybinder = { + remove = function(action) + for i,_ in ipairs(keybinds[action]) do + mp.remove_key_binding(action..tostring(i)) + end + end, + add = function(action, func, repeatable) + for i,key in ipairs(keybinds[action]) do + assert(type(func)=="function", "not a function") + if repeatable then + mp.add_forced_key_binding(key, action..tostring(i), func, "repeatable") + else + mp.add_forced_key_binding(key, action..tostring(i), func) + end + end + end +} + +local fader = { + saved_brtns, + on = function(self) + if fade and not self.saved_brtns then + self.saved_brtns = mp.get_property("brightness") + mp.set_property("brightness", plsbrightness) + end + end, + off = function(self) + if fade and self.saved_brtns then + mp.set_property("brightness", self.saved_brtns) + self.saved_brtns=nil + end + end +} + +local playlister = { +-- pls — список элементов плейлиста + pls, +-- plsfiltered — список индексов выбранных фильтром элементов плейлиста + plsfiltered, + plspos, + wndstart, + wndend, + cursor, + + init = function(self) + if not self.pls then + self.pls = mp.get_property_native("playlist") + end + mp.commandv("stop") + --need to mark first entry non-current (mpv bug?) + if self.pls[1] then + self.pls[1].current = false + end + if favorites and #favorites>0 then + self:sortfavs() + end + pattern = "" + self.plsfiltered = tablekeys(self.pls) + end, + + show = function(self) + local i + local newpos + local msg + --media-title + --playlist t[2].title + + if not self.plsfiltered then + return + end + if not self.plspos then + self.plspos=mp.get_property_native("playlist-pos-1") + --plscount=mp.get_property_native("playlist-count") + end + if not self.wndstart or not self.cursor then + self.wndstart=1 + self.cursor=0 + end + + msg="" + i = self.wndstart + local prefix + while self.plsfiltered[i] and i<=self.wndstart+window-1 do + if self.pls[self.plsfiltered[i]].current then + prefix="*" + elseif i==self.wndstart+self.cursor then + prefix=">" + else + prefix=" " + end + msg = msg..prefix..(self.pls[self.plsfiltered[i]].title or "").."\n" + i=i+1 + end + if self.wndstart>1 then + msg = "...\n"..msg + else + msg = " \n"..msg + end + if self.wndstart+window-1<#self.plsfiltered then + msg = msg.."..." + end + msg="/"..pattern.."\n"..msg + mp.osd_message(msg, osd_time) + end, + + sortfavs = function(self) + --favorites bubbles to the top + local favs={} + local nonfavs={} + for _,v in ipairs(self.pls) do + if in_array(favorites,v.title) then + favs[#favs+1] = v + else + nonfavs[#nonfavs+1] = v + end + end + for i=1,#nonfavs do + favs[#favs+1] = nonfavs[i] + end + self.pls = favs + end, + + filter = function(self) + self.plsfiltered={} + for i,v in ipairs(self.pls) do + if string.match(mylower(v.title),'.*'..prepat(pattern)..'.*') then + table.insert(self.plsfiltered,i) + end + end + self.wndstart=1 + self.cursor=0 + end, + + down = function(self) + if self.cursor >= #self.plsfiltered-1 then return end + if self.cursor0 then + self.cursor=self.cursor-1 + self.show(self) + else + if self.wndstart>1 then + self.wndstart=self.wndstart-1 + self.show(self) + end + end + end, + + play = function(self) + mp.commandv("loadfile",self.pls[self.plsfiltered[self.wndstart+self.cursor]].filename) + if self.plspos then + self.pls[self.plspos].current=false + end + self.plspos=self.plsfiltered[self.wndstart+self.cursor] + self.pls[self.plspos].current=true + end +} + +function add_bindings() + keybinder.add("plsup", up, true) + keybinder.add("plsdown", down, true) + for i,v in ipairs(chars) do + c=string.char(v) + mp.add_forced_key_binding(c, 'search'..v, typing(c),"repeatable") + end + mp.add_forced_key_binding('SPACE', 'search32', typing(' '),"repeatable") + +--[[ mp.add_key_binding('а', 'search1000', typing('а'),"repeatable") + mp.add_key_binding('с', 'search1001', typing('с'),"repeatable")]] + + mp.add_forced_key_binding('BS', 'searchbs', backspace,"repeatable") + keybinder.add("plsenter", play) + for i,v in ipairs(cyr_chars) do + mp.add_forced_key_binding(v, 'search'..i+1000, typing(v),"repeatable") + end +end + +function remove_bindings() + keybinder.remove('plsup') + keybinder.remove('plsdown') + keybinder.remove('plsenter') + for i,v in ipairs(chars) do + c=string.char(v) + mp.remove_key_binding('search'..v) + end + mp.remove_key_binding('search32') + mp.remove_key_binding('searchbs') + for i,v in ipairs(cyr_chars) do + mp.remove_key_binding('search'..i+1000) + end +end + +function activate() + if is_active then + shutdown() + return + else + is_active=true + fader:on() + playlister:show() + add_bindings() + if not timer then + timer=mp.add_periodic_timer(osd_time, shutdown) + timer.oneshot=true + else + resumetimer() + end + end +end + +function tablekeys(t) + local result={} + for i,v in ipairs(t) do + table.insert(result,i) + end + return result +end + +function in_array(array, value) + for _,v in ipairs(array) do + if v==value then + return true + end + end + return false +end + +function mylower(s) + local res,n = string.gsub(s,utf8_char,function (c) + return utf8_uc_lc[c] + end) + return res +end + +function myupper(s) + local res,n = string.gsub(s,utf8_char,function (c) + return utf8_lc_uc[c] + end) + return res +end + +function prepat(s) +--prepare nocase and magic chars + s = string.gsub(s, "[%^%$%(%)%%%.%[%]%*%+%-%?]",function (c) + return '%'..c + end) +--[[ s = string.gsub(s, utf8_char, function (c) + return string.format("[%s%s]", utf8_uc_lc[c] or c, utf8_lc_uc[c] or c) + end)]] + return s +end + +function resumetimer() + timer:kill() + timer:resume() +end + +function typing(char) + return function() + local c=string.lower(char) + pattern = pattern..c + playlister:filter() + playlister:show() + resumetimer() + end +end + +function backspace() + if string.len(pattern)>0 then +-- pattern = string.sub(pattern,1,-2) +-- for unicode + pattern = string.match(pattern,"(.*)"..utf8_char.."$") + playlister:filter() + playlister:show() + resumetimer() + end +end + +function play() +-- mp.commandv("playlist-move", wndstart+cursor, 1) +-- mp.commandv("playlist-clear") +-- mp.commandv("playlist-next") + fader:off() + playlister:play() + playlister:show() + resumetimer() +end + +function shutdown() + fader:off() + remove_bindings() + is_active=false + mp.osd_message("", 1) +end + +function down() + fader:on() + playlister:down() + resumetimer() +end + +function up() + fader:on() + playlister:up() + resumetimer() +end + +function on_start_file() + if is_playlist_loaded then + playlister:init() + mp.unregister_event(on_start_file) + activate() + else + is_playlist_loaded = true + end +end + +if mp.get_opt("iptv") then + mp.set_property_bool("idle", true) + mp.set_property_bool("force-window", true) + mp.register_event("start-file", on_start_file) + keybinder.add("activate", activate) +end + diff --git a/.config/newsboat/config b/.config/newsboat/config new file mode 100644 index 0000000..5b0fed3 --- /dev/null +++ b/.config/newsboat/config @@ -0,0 +1,26 @@ +auto-reload yes +refresh-on-startup yes +reload-time 3600 +notify-program "/home/yorune/.local/bin/notify-program" +notify-always yes + +unbind-key o +bind-key o open-in-browser + +macro , open-in-browser +macro p set browser "mpv --ytdl-format='bestvideo[ext=mp4][height<=?1080]+bestaudio[ext=m4a]' --no-resume-playback" ; open-in-browser ; set browser "/home/yorune/.local/bin/browser-x -new-tab %u" + +unbind-key n +unbind-key p +bind-key n next +bind-key p prev + +color listnormal cyan default +color listfocus black yellow standout bold +color listnormal_unread blue default +color listfocus_unread yellow default bold +color info red black bold +color article white default bold + +browser "/home/yorune/.local/bin/browser-x -new-tab %u" +player mpv diff --git a/.config/newsboat/urls b/.config/newsboat/urls new file mode 100644 index 0000000..9c46c95 --- /dev/null +++ b/.config/newsboat/urls @@ -0,0 +1,70 @@ +=====================NEWS==================================== +https://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=infoKONINpl&media_type=all&limit=-1&format=Mrss "~infoKONIN - Facebook" +shttps://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=gminakleczew&media_type=all&limit=-1&format=Mrss "~Gmina Kleczew - Facebook" +https://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=portalLM&media_type=all&limit=-1&format=Mrss "~Portal lm.pl - Facebook" +https://nitter.net/MZ_GOV_PL/rss "~MZ_GOV_PL - Twitter" +http://www.epoznan.pl/rss.php "~Epoznan - Website" +http://rss.slashdot.org/Slashdot/slashdotMain "SlashDot" +https://sekurak.pl/feed/ "~Sekurak" +https://feeds.feedburner.com/niebezpiecznik/ "~Niebezpiecznik" +https://www.cybsecurity.org/feed/podcast/ "~CybSecurity" +============================================================ +https://bugs.gentoo.org/buglist.cgi?bug_status=UNCONFIRMED&chfieldfrom=24h&ctype=atom&title=NEW&list_id=4468306&order=changeddate%20DESC%2Cbug_status%2Cpriority%2Cassigned_to%2Cbug_id&query_format=advanced Gentoo +https://bugs.gentoo.org/buglist.cgi?component=Package%20issues&product=GURU&query_format=advanced&resolution=---&title=Bug%20List&ctype=atom GURU-BUGS +https://bugs.gentoo.org/buglist.cgi?component=Trusted%20Contributor%20ascension&product=GURU&query_format=advanced&resolution=---&title=Bug%20List&ctype=atom +https://www.reddit.com/r/gentoo/new.rss "~Gentoo - Reddit" +============================================================ +http://rss.uptimerobot.com/u674346-753d02ab763f3725330347d4419a7569 "~UpTimeRoboot" +=====================ANIME=================================== +https://mirror.animetosho.org/feed/rss2?only_tor=1&q=%5BHorribleSubs%5D%20Sword%20Art%20Online%20720p&filter%5B0%5D%5Bt%5D=nyaa_class&filter%5B0%5D%5Bv%5D=trusted "~Sword Art Online" +=====================YOUTUBE================================= +https://www.youtube.com/feeds/videos.xml?channel_id=UCzuvRWjh7k1SZm1RvqvIx4w "~Krzysztof Gonciarz" +https://www.youtube.com/feeds/videos.xml?channel_id=UC3QOjSZmJuYgb98BAzKfzig "~Zapytaj Beczkę" +https://www.youtube.com/feeds/videos.xml?channel_id=UC2eYFnH61tmytImy1mTYvhA "~Luke Smith" +https://www.youtube.com/feeds/videos.xml?channel_id=UC2qQkGwReBfucb6yWr7idRA "~Asia Ladowska" +https://www.youtube.com/feeds/videos.xml?channel_id=UC5ZSM0kOGhtLdtZiqM2noWw "~StudyWithInSpo" +https://www.youtube.com/feeds/videos.xml?channel_id=UC84AaEtr7hlaYYwXDSKAbNg "~Ignacy z Japonii" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8JbbaZ_jgdsoUqrZ2bXtQQ "~Lekko Stronniczy" +https://www.youtube.com/feeds/videos.xml?channel_id=UC8q23MpiyWjv9vd4r85oj1A "~Daniel Rakowiecki" +https://www.youtube.com/feeds/videos.xml?channel_id=UC9Vi25H6lQvPqcepJCJ3sEg "~Emma" +https://www.youtube.com/feeds/videos.xml?channel_id=UCAD-xOOaUI6N7Uq9laOVbcw "~Rene Rene" +https://www.youtube.com/feeds/videos.xml?channel_id=UCAEQl0BbYrPyTnsWVBJuIqQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCaG-Uk3PSpHexeUJaJC_TPw +https://www.youtube.com/feeds/videos.xml?channel_id=UCAkBZtIl-a0fBLcY7JCPSQw +https://www.youtube.com/feeds/videos.xml?channel_id=UCB-4hoTjsE3VIFI862C5SZA +https://www.youtube.com/feeds/videos.xml?channel_id=UCE5Au4LfcBHxTQR_yLbncrQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCe6nK69Yc1zna7QSJEfA9pw +https://www.youtube.com/feeds/videos.xml?channel_id=UCEbYhDd6c6vngsF5PQpFVWg +https://www.youtube.com/feeds/videos.xml?channel_id=UCELsE3w85wy7hXFmU2eRn_w +https://www.youtube.com/feeds/videos.xml?channel_id=UCfegHHvKywKjJ69Ymcb6j0w +https://www.youtube.com/feeds/videos.xml?channel_id=UCFGud6qsbaAjuP6WaXAJj3Q +https://www.youtube.com/feeds/videos.xml?channel_id=UCH2OHdQF5c2r2M_l_vPiXRA +https://www.youtube.com/feeds/videos.xml?channel_id=UCIDf8pzX2oZmjXtqMwHn0xg +https://www.youtube.com/feeds/videos.xml?channel_id=UCIj6yjWVKPKO5zNLBjQ8Beg +https://www.youtube.com/feeds/videos.xml?channel_id=UCJ24N4O0bP7LGLBDvye7oCA +https://www.youtube.com/feeds/videos.xml?channel_id=UCJkmbV_cndEe6Af8b5qVyoQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCJLLl6AraX1POemgLfhirwg +https://www.youtube.com/feeds/videos.xml?channel_id=UCjS2aGCvsnhExcWRAI8T4Pw +https://www.youtube.com/feeds/videos.xml?channel_id=UCMsi03EhTUsUs2OtPus6XDQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCMTkC2dA25yFsmtaevpYWfg +https://www.youtube.com/feeds/videos.xml?channel_id=UCnOo8dtBgwJbdwl4HxqPHwg +https://www.youtube.com/feeds/videos.xml?channel_id=UCO8DQrSp5yEP937qNqTooOw +https://www.youtube.com/feeds/videos.xml?channel_id=UCoJjEsNkU-Md3xB6BursT0Q +https://www.youtube.com/feeds/videos.xml?channel_id=UCoXxgqIOTa8qCM7Hd7RiURw +https://www.youtube.com/feeds/videos.xml?channel_id=UCPc_saLjikyw-1sscFsHIsA +https://www.youtube.com/feeds/videos.xml?channel_id=UCPKgIhTC3BdkAwMw6s-GEug +https://www.youtube.com/feeds/videos.xml?channel_id=UCRxAgfYexGLlu1WHGIMUDqw +https://www.youtube.com/feeds/videos.xml?channel_id=UCs02jgjMVeirmDHaxAXApkg +https://www.youtube.com/feeds/videos.xml?channel_id=UCsnGwSIHyoYN0kiINAGUKxg +https://www.youtube.com/feeds/videos.xml?channel_id=UCv9wKjBogC5AVG54s_Imn0A +https://www.youtube.com/feeds/videos.xml?channel_id=UCvbEYH_h8k-hHq2oS-7zIdQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCVbn813ctsoChuTT4LuLqrA +https://www.youtube.com/feeds/videos.xml?channel_id=UCVVt5tbZEzfQSIsHUh3b_vg +https://www.youtube.com/feeds/videos.xml?channel_id=UCw3T7SvPYDHCkdMhC0NcKsw +https://www.youtube.com/feeds/videos.xml?channel_id=UCWcz2SQMCcijkVta0hSERIA +https://www.youtube.com/feeds/videos.xml?channel_id=UCWFKCr40YwOZQx8FHU_ZqqQ +https://www.youtube.com/feeds/videos.xml?channel_id=UCXRJfVWEjv9qxooSbOkFmkw +https://www.youtube.com/feeds/videos.xml?channel_id=UCXuqSBlHAE6Xw-yeJA0Tunw +https://www.youtube.com/feeds/videos.xml?channel_id=UCZgyxIrQnTDLwwEr9VSkoPA +https://www.youtube.com/feeds/videos.xml?channel_id=UCZKqIdmPtIU6DOTmWxzudKw +============================================================= diff --git a/.config/ranger/rc.conf b/.config/ranger/rc.conf new file mode 100644 index 0000000..ca4fc2c --- /dev/null +++ b/.config/ranger/rc.conf @@ -0,0 +1,567 @@ +#set viewmode miller +set viewmode multipane +set column_ratios 1,3,4 +set hidden_filter ^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$ +set show_hidden false + +# Ask for a confirmation when running the "delete" command? +# Valid values are "always", "never", "multiple" (default) +# With "multiple", ranger will ask only if you delete multiple files at once. +#set confirm_on_delete multiple +set confirm_on_delete never +set use_preview_script true +set automatically_count_files true +set open_all_images true +set vcs_aware false +set vcs_backend_git enabled +set vcs_backend_hg disabled +set vcs_backend_bzr disabled +set vcs_backend_svn disabled +set preview_images true +set preview_images_method urxvt +set w3m_delay 0.02 +set iterm2_font_width 8 +set iterm2_font_height 11 +set unicode_ellipsis false +set bidi_support false +set show_hidden_bookmarks true +set colorscheme default +set preview_files true +set preview_directories true +set collapse_preview true +set save_console_history true +set status_bar_on_top false +set draw_progress_bar_in_status_bar true +set draw_borders true +set respect draw_borders +set dirname_in_tabs false +set mouse_enabled true +set display_size_in_main_column true +set display_size_in_status_bar true + +# Display the free disk space in the status bar? +set display_free_space_in_status_bar true + +# Display files tags in all columns or only in main column? +set display_tags_in_all_columns true + +# Set a title for the window? +set update_title false + +# Set the title to "ranger" in the tmux program? +set update_tmux_title true + +# Shorten the title if it gets long? The number defines how many +# directories are displayed at once, 0 turns off this feature. +set shorten_title 3 + +# Show hostname in titlebar? +set hostname_in_titlebar false + +# Abbreviate $HOME with ~ in the titlebar (first line) of ranger? +set tilde_in_titlebar true + +# How many directory-changes or console-commands should be kept in history? +set max_history_size 20 +set max_console_history_size 50 + +# Try to keep so much space between the top/bottom border when scrolling: +set scroll_offset 10 + +# Flush the input after each key hit? (Noticeable when ranger lags) +set flushinput true + +# Padding on the right when there's no preview? +# This allows you to click into the space to run the file. +set padding_right true + +# Save bookmarks (used with mX and `X) instantly? +# This helps to synchronize bookmarks between multiple ranger +# instances but leads to *slight* performance loss. +# When false, bookmarks are saved when ranger is exited. +set autosave_bookmarks true + +# Save the "`" bookmark to disk. This can be used to switch to the last +# directory by typing "``". +set save_backtick_bookmark true + +# You can display the "real" cumulative size of directories by using the +# command :get_cumulative_size or typing "dc". The size is expensive to +# calculate and will not be updated automatically. You can choose +# to update it automatically though by turning on this option: +set autoupdate_cumulative_size false + +# Turning this on makes sense for screen readers: +set show_cursor false + +# One of: size, natural, basename, atime, ctime, mtime, type, random +set sort natural + +# Additional sorting options +set sort_reverse false +set sort_case_insensitive true +set sort_directories_first true +set sort_unicode false + +# Enable this if key combinations with the Alt Key don't work for you. +# (Especially on xterm) +set xterm_alt_key false + +# Whether to include bookmarks in cd command +set cd_bookmarks true + +# Changes case sensitivity for the cd command tab completion +set cd_tab_case sensitive + +# Use fuzzy tab completion with the "cd" command. For example, +# ":cd /u/lo/b" expands to ":cd /usr/local/bin". +set cd_tab_fuzzy false +set preview_max_size 0 +set hint_collapse_threshold 10 +set show_selection_in_titlebar true +set idle_delay 2000 +set metadata_deep_search false +set clear_filters_on_dir_change false +set line_numbers false +set relative_current_zero false +set one_indexed false +set save_tabs_on_exit false +set wrap_scroll false +set global_inode_type_filter +set freeze_files false + +# =================================================================== +# == My settings +# =================================================================== + +setlocal path=~/Screenshots sort mtime +setlocal path=~/Screenshots sort_reverse False + + +# =================================================================== +# == Local Options +# =================================================================== +# You can set local options that only affect a single directory. + +# Examples: +# setlocal path=~/downloads sort mtime + +# =================================================================== +# == Command Aliases in the Console +# =================================================================== + +alias e edit +alias q quit +alias q! quit! +alias qa quitall +alias qa! quitall! +alias qall quitall +alias qall! quitall! +alias setl setlocal + +alias filter scout -prts +alias find scout -aets +alias mark scout -mr +alias unmark scout -Mr +alias search scout -rs +alias search_inc scout -rts +alias travel scout -aefklst + +# =================================================================== +# == Define keys for the browser +# =================================================================== + +# Basic +map bg shell set-wallpaper %f +map bc shell wall -i %f +map bh cd ~ + +map Q quitall +map q quit +copymap q ZZ ZQ + +map R reload_cwd +map F set freeze_files! +map reset +map redraw_window +map abort +map change_mode normal +map ~ set viewmode! + +map i display_file +map ? help +map W display_log +map w taskview_open +map S shell $SHELL + +map : console +map ; console +map ! console shell%space +map @ console -p6 shell %%s +map # console shell -p%space +map s console shell%space +map r chain draw_possible_programs; console open_with%%space +map f console find%space +map cd console cd%space + +map chain console; eval fm.ui.console.history_move(-1) + +# Change the line mode +map Mf linemode filename +map Mi linemode fileinfo +map Mm linemode mtime +map Mp linemode permissions +map Ms linemode sizemtime +map Mt linemode metatitle + +# Tagging / Marking +map t tag_toggle +map ut tag_remove +map mark_files toggle=True +map v mark_files all=True toggle=True +map uv mark_files all=True val=False +map V toggle_visual_mode +map uV toggle_visual_mode reverse=True + +# For the nostalgics: Midnight Commander bindings +map help +map rename_append +map display_file +map edit +map copy +map cut +map console mkdir%space +map console delete +map exit + +# In case you work on a keyboard with dvorak layout +map move up=1 +map move down=1 +map move left=1 +map move right=1 +map move to=0 +map move to=-1 +map move down=1 pages=True +map move up=1 pages=True +map move right=1 +#map console delete +map console touch%space + +# VIM-like +copymap k +copymap j +copymap h +copymap l +copymap gg +copymap G +copymap +copymap + +map J move down=0.5 pages=True +map K move up=0.5 pages=True +copymap J +copymap K + +# Jumping around +map H history_go -1 +map L history_go 1 +map ] move_parent 1 +map [ move_parent -1 +map } traverse +map { traverse_backwards +map ) jump_non + +#DEFAULT MOVEMENT +map ge cd /etc +map gh cd ~ +map gu cd /usr +#map gl cd -r . +map gL cd -r %f +#map gv cd /var +map gM cd /mnt +map gr cd / +map gR eval fm.cd(ranger.RANGERDIR) +map g? cd /usr/share/doc/ranger + +# External Programs +map E edit +map du shell -p du --max-depth=1 -h --apparent-size +map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh +map yp yank path +map yd yank dir +map yn yank name +map y. yank name_without_extension + +# Filesystem Operations +map = chmod + +map cw console rename%space +map a rename_append +map A eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%")) +map I eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"), position=7) + +map pp paste +map po paste overwrite=True +map pP paste append=True +map pO paste overwrite=True append=True +map pl paste_symlink relative=False +map pL paste_symlink relative=True +map phl paste_hardlink +map pht paste_hardlinked_subtree + +map dD console delete + +map dd cut +map ud uncut +map da cut mode=add +map dr cut mode=remove +map dt cut mode=toggle + +map yy copy +map uy uncut +map ya copy mode=add +map yr copy mode=remove +map yt copy mode=toggle + +# Temporary workarounds +map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier) +map dG eval fm.cut(dirarg=dict(to=-1), narg=quantifier) +map dj eval fm.cut(dirarg=dict(down=1), narg=quantifier) +map dk eval fm.cut(dirarg=dict(up=1), narg=quantifier) +map ygg eval fm.copy(dirarg=dict(to=0), narg=quantifier) +map yG eval fm.copy(dirarg=dict(to=-1), narg=quantifier) +map yj eval fm.copy(dirarg=dict(down=1), narg=quantifier) +map yk eval fm.copy(dirarg=dict(up=1), narg=quantifier) + +# Searching +map / console search%space +map n search_next +map N search_next forward=False +map ct search_next order=tag +map cs search_next order=size +map ci search_next order=mimetype +map cc search_next order=ctime +map cm search_next order=mtime +map ca search_next order=atime + +# Tabs +map tab_new +map tab_close +map tab_move 1 +map tab_move -1 +map tab_move 1 +map tab_move -1 +map uq tab_restore +map tab_open 1 +map tab_open 2 +map tab_open 3 +map tab_open 4 +map tab_open 5 +map tab_open 6 +map tab_open 7 +map tab_open 8 +map tab_open 9 +map tab_shift 1 +map tab_shift -1 + +# Sorting +map or set sort_reverse! +map oz set sort=random +map os chain set sort=size; set sort_reverse=False +map ob chain set sort=basename; set sort_reverse=False +map on chain set sort=natural; set sort_reverse=False +map om chain set sort=mtime; set sort_reverse=False +map oc chain set sort=ctime; set sort_reverse=False +map oa chain set sort=atime; set sort_reverse=False +map ot chain set sort=type; set sort_reverse=False +map oe chain set sort=extension; set sort_reverse=False + +map oS chain set sort=size; set sort_reverse=True +map oB chain set sort=basename; set sort_reverse=True +map oN chain set sort=natural; set sort_reverse=True +map oM chain set sort=mtime; set sort_reverse=True +map oC chain set sort=ctime; set sort_reverse=True +map oA chain set sort=atime; set sort_reverse=True +map oT chain set sort=type; set sort_reverse=True +map oE chain set sort=extension; set sort_reverse=True + +map dc get_cumulative_size + +# Settings +map zc set collapse_preview! +map zd set sort_directories_first! +map zh set show_hidden! +map set show_hidden! +copymap +copymap +map zI set flushinput! +map zi set preview_images! +map zm set mouse_enabled! +map zp set preview_files! +map zP set preview_directories! +map zs set sort_case_insensitive! +map zu set autoupdate_cumulative_size! +map zv set use_preview_script! +map zf console filter%space +copymap zf zz + +# Filter stack +map .n console filter_stack add name%space +map .m console filter_stack add mime%space +map .d filter_stack add type d +map .f filter_stack add type f +map .l filter_stack add type l +map .| filter_stack add or +map .& filter_stack add and +map .! filter_stack add not +map .r console filter_stack rotate +map .c filter_stack clear +map .* filter_stack decompose +map .p filter_stack pop +map .. filter_stack show + +# Bookmarks +map ` enter_bookmark %any +map ' enter_bookmark %any +map m set_bookmark %any +map um unset_bookmark %any + +map m draw_bookmarks +copymap m um ` ' + +# Generate all the chmod bindings with some python help: +eval for arg in "rwxXst": cmd("map +u{0} shell -f chmod u+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +g{0} shell -f chmod g+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +o{0} shell -f chmod o+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +a{0} shell -f chmod a+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +{0} shell -f chmod u+{0} %s".format(arg)) + +eval for arg in "rwxXst": cmd("map -u{0} shell -f chmod u-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -g{0} shell -f chmod g-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -o{0} shell -f chmod o-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -a{0} shell -f chmod a-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -{0} shell -f chmod u-{0} %s".format(arg)) + +# =================================================================== +# == Define keys for the console +# =================================================================== +# Note: Unmapped keys are passed directly to the console. + +# Basic +cmap eval fm.ui.console.tab() +cmap eval fm.ui.console.tab(-1) +cmap eval fm.ui.console.close() +cmap eval fm.ui.console.execute() +cmap redraw_window + +copycmap +copycmap + +# Move around +cmap eval fm.ui.console.history_move(-1) +cmap eval fm.ui.console.history_move(1) +cmap eval fm.ui.console.move(left=1) +cmap eval fm.ui.console.move(right=1) +cmap eval fm.ui.console.move(right=0, absolute=True) +cmap eval fm.ui.console.move(right=-1, absolute=True) +cmap eval fm.ui.console.move_word(left=1) +cmap eval fm.ui.console.move_word(right=1) + +copycmap +copycmap + +# Line Editing +cmap eval fm.ui.console.delete(-1) +cmap eval fm.ui.console.delete(0) +cmap eval fm.ui.console.delete_word() +cmap eval fm.ui.console.delete_word(backward=False) +cmap eval fm.ui.console.delete_rest(1) +cmap eval fm.ui.console.delete_rest(-1) +cmap eval fm.ui.console.paste() + +# And of course the emacs way +copycmap +copycmap +copycmap +copycmap +copycmap +copycmap +copycmap +copycmap +copycmap + +# Note: There are multiple ways to express backspaces. (code 263) +# and (code 127). To be sure, use both. +copycmap + +# This special expression allows typing in numerals: +cmap false + +# =================================================================== +# == Pager Keybindings +# =================================================================== + +# Movement +pmap pager_move down=1 +pmap pager_move up=1 +pmap pager_move left=4 +pmap pager_move right=4 +pmap pager_move to=0 +pmap pager_move to=-1 +pmap pager_move down=1.0 pages=True +pmap pager_move up=1.0 pages=True +pmap pager_move down=0.5 pages=True +pmap pager_move up=0.5 pages=True + +copypmap k +copypmap j +copypmap h +copypmap l +copypmap g +copypmap G +copypmap d +copypmap u +copypmap n f +copypmap p b + +# Basic +pmap redraw_window +pmap pager_close +copypmap q Q i +pmap E edit_file + +# =================================================================== +# == Taskview Keybindings +# =================================================================== + +# Movement +tmap taskview_move up=1 +tmap taskview_move down=1 +tmap taskview_move to=0 +tmap taskview_move to=-1 +tmap taskview_move down=1.0 pages=True +tmap taskview_move up=1.0 pages=True +tmap taskview_move down=0.5 pages=True +tmap taskview_move up=0.5 pages=True + +copytmap k +copytmap j +copytmap g +copytmap G +copytmap u +copytmap n f +copytmap p b + +# Changing priority and deleting tasks +tmap J eval -q fm.ui.taskview.task_move(-1) +tmap K eval -q fm.ui.taskview.task_move(0) +tmap dd eval -q fm.ui.taskview.task_remove() +tmap eval -q fm.ui.taskview.task_move(-1) +tmap eval -q fm.ui.taskview.task_move(0) +tmap eval -q fm.ui.taskview.task_remove() + +# Basic +tmap redraw_window +tmap taskview_close +copytmap q Q w + diff --git a/.config/ranger/rifle.conf b/.config/ranger/rifle.conf new file mode 100644 index 0000000..c524a9c --- /dev/null +++ b/.config/ranger/rifle.conf @@ -0,0 +1,210 @@ +ext jpg = feh --scale-down "$@" +#ext doc = openoffice4 "$@" +#ext odt = openoffice4 "$@" + +#------------------------------------------- +# Websites +#------------------------------------------- +# Rarely installed browsers get higher priority; It is assumed that if you +# install a rare browser, you probably use it. Firefox/konqueror/w3m on the +# other hand are often only installed as fallback browsers. +ext x?html?, has surf, X, flag f = surf -- file://"$1" +ext x?html?, has vimprobable, X, flag f = vimprobable -- "$@" +ext x?html?, has vimprobable2, X, flag f = vimprobable2 -- "$@" +ext x?html?, has qutebrowser, X, flag f = qutebrowser -- "$@" +ext x?html?, has dwb, X, flag f = dwb -- "$@" +ext x?html?, has jumanji, X, flag f = jumanji -- "$@" +ext x?html?, has luakit, X, flag f = luakit -- "$@" +ext x?html?, has uzbl, X, flag f = uzbl -- "$@" +ext x?html?, has uzbl-tabbed, X, flag f = uzbl-tabbed -- "$@" +ext x?html?, has uzbl-browser, X, flag f = uzbl-browser -- "$@" +ext x?html?, has uzbl-core, X, flag f = uzbl-core -- "$@" +ext x?html?, has midori, X, flag f = midori -- "$@" +ext x?html?, has chromium-browser, X, flag f = chromium-browser -- "$@" +ext x?html?, has chromium, X, flag f = chromium -- "$@" +ext x?html?, has google-chrome, X, flag f = google-chrome -- "$@" +ext x?html?, has opera, X, flag f = opera -- "$@" +ext x?html?, has firefox, X, flag f = firefox -- "$@" +ext x?html?, has seamonkey, X, flag f = seamonkey -- "$@" +ext x?html?, has iceweasel, X, flag f = iceweasel -- "$@" +ext x?html?, has epiphany, X, flag f = epiphany -- "$@" +ext x?html?, has konqueror, X, flag f = konqueror -- "$@" +ext x?html?, has elinks, terminal = elinks "$@" +ext x?html?, has links2, terminal = links2 "$@" +ext x?html?, has links, terminal = links "$@" +ext x?html?, has lynx, terminal = lynx -- "$@" +ext x?html?, has w3m, terminal = w3m "$@" + +#------------------------------------------- +# Misc +#------------------------------------------- +# Define the "editor" for text files as first action +mime ^text, label editor = ${VISUAL:-$EDITOR} -- "$@" +mime ^text, label pager = "$PAGER" -- "$@" +!mime ^text, label editor, ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@" +!mime ^text, label pager, ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@" + +ext 1 = man "$1" +ext s[wmf]c, has zsnes, X = zsnes "$1" +ext s[wmf]c, has snes9x-gtk,X = snes9x-gtk "$1" +ext nes, has fceux, X = fceux "$1" +ext exe = wine "$1" +name ^[mM]akefile$ = make + +#-------------------------------------------- +# Code +#------------------------------------------- +ext py = python -- "$1" +ext pl = perl -- "$1" +ext rb = ruby -- "$1" +ext js = node -- "$1" +ext sh = sh -- "$1" +ext php = php -- "$1" + +#-------------------------------------------- +# Audio without X +#------------------------------------------- +mime ^audio|ogg$, terminal, has mpv = mpv -- "$@" +mime ^audio|ogg$, terminal, has mplayer2 = mplayer2 -- "$@" +mime ^audio|ogg$, terminal, has mplayer = mplayer -- "$@" +ext midi?, terminal, has wildmidi = wildmidi -- "$@" + +#-------------------------------------------- +# Video/Audio with a GUI +#------------------------------------------- +mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@" +mime ^video|audio, has smplayer, X, flag f = smplayer "$@" +mime ^video, has mpv, X, flag f = mpv -- "$@" +mime ^video, has mpv, X, flag f = mpv --fs -- "$@" +mime ^video, has mplayer2, X, flag f = mplayer2 -- "$@" +mime ^video, has mplayer2, X, flag f = mplayer2 -fs -- "$@" +mime ^video, has mplayer, X, flag f = mplayer -- "$@" +mime ^video, has mplayer, X, flag f = mplayer -fs -- "$@" +mime ^video|audio, has vlc, X, flag f = vlc -- "$@" +mime ^video|audio, has totem, X, flag f = totem -- "$@" +mime ^video|audio, has totem, X, flag f = totem --fullscreen -- "$@" + +#-------------------------------------------- +# Video without X: +#------------------------------------------- +mime ^video, terminal, !X, has mpv = mpv -- "$@" +mime ^video, terminal, !X, has mplayer2 = mplayer2 -- "$@" +mime ^video, terminal, !X, has mplayer = mplayer -- "$@" + +#------------------------------------------- +# Documents +#------------------------------------------- +ext pdf, has llpp, X, flag f = llpp "$@" +ext pdf, has zathura, X, flag f = zathura -- "$@" +ext pdf, has mupdf, X, flag f = mupdf "$@" +ext pdf, has mupdf-x11,X, flag f = mupdf-x11 "$@" +ext pdf, has apvlv, X, flag f = apvlv -- "$@" +ext pdf, has xpdf, X, flag f = xpdf -- "$@" +ext pdf, has evince, X, flag f = evince -- "$@" +ext pdf, has atril, X, flag f = atril -- "$@" +ext pdf, has okular, X, flag f = okular -- "$@" +ext pdf, has epdfview, X, flag f = epdfview -- "$@" +ext pdf, has qpdfview, X, flag f = qpdfview "$@" +ext pdf, has open, X, flag f = open "$@" + +ext docx?, has catdoc, terminal = catdoc -- "$@" | "$PAGER" + +ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has gnumeric, X, flag f = gnumeric -- "$@" +ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has kspread, X, flag f = kspread -- "$@" +ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has libreoffice, X, flag f = libreoffice "$@" +ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has soffice, X, flag f = soffice "$@" +ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has ooffice, X, flag f = ooffice "$@" + +ext djvu, has zathura,X, flag f = zathura -- "$@" +ext djvu, has evince, X, flag f = evince -- "$@" +ext djvu, has atril, X, flag f = atril -- "$@" +ext djvu, has djview, X, flag f = djview -- "$@" + +ext epub, has ebook-viewer, X, flag f = ebook-viewer -- "$@" +ext mobi, has ebook-viewer, X, flag f = ebook-viewer -- "$@" + +#------------------------------------------- +# Image Viewing: +#------------------------------------------- +mime ^image/svg, has inkscape, X, flag f = inkscape -- "$@" +mime ^image/svg, has display, X, flag f = display -- "$@" + +mime ^image, has pqiv, X, flag f = pqiv -- "$@" +mime ^image, has sxiv, X, flag f = sxiv -- "$@" +mime ^image, has feh, X, flag f = feh -- "$@" +mime ^image, has mirage, X, flag f = mirage -- "$@" +mime ^image, has ristretto, X, flag f = ristretto "$@" +mime ^image, has eog, X, flag f = eog -- "$@" +mime ^image, has eom, X, flag f = eom -- "$@" +mime ^image, has nomacs, X, flag f = nomacs -- "$@" +mime ^image, has geeqie, X, flag f = geeqie -- "$@" +mime ^image, has gwenview, X, flag f = gwenview -- "$@" +mime ^image, has gimp, X, flag f = gimp -- "$@" +ext xcf, X, flag f = gimp -- "$@" + +#------------------------------------------- +# Archives +#------------------------------------------- + +# avoid password prompt by providing empty password +ext 7z, has 7z = 7z -p l "$@" | "$PAGER" +# This requires atool +ext ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --list --each -- "$@" | "$PAGER" +ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --list --each -- "$@" | "$PAGER" +ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --extract --each -- "$@" +ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --extract --each -- "$@" + +# Listing and extracting archives without atool: +ext tar|gz|bz2|xz, has tar = tar vvtf "$1" | "$PAGER" +ext tar|gz|bz2|xz, has tar = for file in "$@"; do tar vvxf "$file"; done +ext bz2, has bzip2 = for file in "$@"; do bzip2 -dk "$file"; done +ext zip, has unzip = unzip -l "$1" | less +ext zip, has unzip = for file in "$@"; do unzip -d "${file%.*}" "$file"; done +ext ace, has unace = unace l "$1" | less +ext ace, has unace = for file in "$@"; do unace e "$file"; done +ext rar, has unrar = unrar l "$1" | less +ext rar, has unrar = for file in "$@"; do unrar x "$file"; done + +#------------------------------------------- +# Flag t fallback terminals +#------------------------------------------- +# Rarely installed terminal emulators get higher priority; It is assumed that +# if you install a rare terminal emulator, you probably use it. +# gnome-terminal/konsole/xterm on the other hand are often installed as part of +# a desktop environment or as fallback terminal emulators. +mime ^ranger/x-terminal-emulator, has terminology = terminology -e "$@" +mime ^ranger/x-terminal-emulator, has kitty = kitty -- "$@" +mime ^ranger/x-terminal-emulator, has alacritty = alacritty -e "$@" +mime ^ranger/x-terminal-emulator, has sakura = sakura -e "$@" +mime ^ranger/x-terminal-emulator, has lilyterm = lilyterm -e "$@" +#mime ^ranger/x-terminal-emulator, has cool-retro-term = cool-retro-term -e "$@" +mime ^ranger/x-terminal-emulator, has termite = termite -x '"$@"' +#mime ^ranger/x-terminal-emulator, has yakuake = yakuake -e "$@" +mime ^ranger/x-terminal-emulator, has guake = guake -ne "$@" +mime ^ranger/x-terminal-emulator, has tilda = tilda -c "$@" +mime ^ranger/x-terminal-emulator, has st = st -e "$@" +mime ^ranger/x-terminal-emulator, has terminator = terminator -x "$@" +mime ^ranger/x-terminal-emulator, has urxvt = urxvt -e "$@" +mime ^ranger/x-terminal-emulator, has pantheon-terminal = pantheon-terminal -e "$@" +mime ^ranger/x-terminal-emulator, has lxterminal = lxterminal -e "$@" +mime ^ranger/x-terminal-emulator, has mate-terminal = mate-terminal -x "$@" +mime ^ranger/x-terminal-emulator, has xfce4-terminal = xfce4-terminal -x "$@" +mime ^ranger/x-terminal-emulator, has konsole = konsole -e "$@" +mime ^ranger/x-terminal-emulator, has gnome-terminal = gnome-terminal -- "$@" +mime ^ranger/x-terminal-emulator, has xterm = xterm -e "$@" + +#------------------------------------------- +# Misc +#------------------------------------------- +label wallpaper, number 11, mime ^image, has feh, X = feh --bg-scale "$1" +label wallpaper, number 12, mime ^image, has feh, X = feh --bg-tile "$1" +label wallpaper, number 13, mime ^image, has feh, X = feh --bg-center "$1" +label wallpaper, number 14, mime ^image, has feh, X = feh --bg-fill "$1" + +# Define the editor for non-text files + pager as last action + !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ask +label editor, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@" +label pager, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@" + +# The very last action, so that it's never triggered accidentally, is to execute a program: +mime application/x-executable = "$1" diff --git a/.emacs.d/emacs-custom.el b/.emacs.d/emacs-custom.el new file mode 100644 index 0000000..b89167d --- /dev/null +++ b/.emacs.d/emacs-custom.el @@ -0,0 +1,14 @@ +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(custom-safe-themes + '("2dff5f0b44a9e6c8644b2159414af72261e38686072e063aa66ee98a2faecf0e" default)) + '(markdown-command "/usr/bin/pandoc")) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + ) diff --git a/.emacs.d/init.el b/.emacs.d/init.el new file mode 100644 index 0000000..7cd6534 --- /dev/null +++ b/.emacs.d/init.el @@ -0,0 +1,529 @@ +;Packages + +;; package archives +(require 'package) +(setq package-enable-at-startup nil) +(setq package-archives + '( + ("melpa" . "https://melpa.org/packages/") + ("ELPA" . "http://tromey.com/elpa/") + ("gnu" . "http://elpa.gnu.org/packages/") + ("ORG" . "https://orgmode.org/elpa/") + ) + ) +(package-initialize) +(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3") + +;; Remove welcome screen +(setq inhibit-startup-screen t) + +;; Disable menu +(menu-bar-mode 0) + +;; Enable IDO mode +(setq ido-enable-flex-matching t) +;;(setq ido-everywhere t) +;;(ido-mode 1) +(global-set-key (kbd "C-x b") 'ido-switch-buffer) + +;; Remove working cl +(require 'cl-lib) +(setq byte-compile-warnings '(cl-functions)) +(advice-add 'sh-set-shell :around + (lambda (orig-fun &rest args) + (cl-letf (((symbol-function 'message) #'ignore)) + (apply orig-fun args)))) + +;; install use-package +(unless (package-installed-p 'use-package) + (package-refresh-contents) + (package-install 'use-package) + ) + +;; Set path to store "custom-set" +(setq custom-file "~/.emacs.d/emacs-custom.el") + +;; Enable awesome-tab-mode +(add-to-list 'load-path (expand-file-name "~/.emacs.d/plugins")) +(require 'awesome-tab) +(awesome-tab-mode t) + +(use-package awesome-tab + :load-path "~/.emacs.d/plugins" + :config + (awesome-tab-mode t)) +(awesome-tab-mode t) + +(global-set-key (kbd "C-x j") 'awesome-tab-backward-tab) +(global-set-key (kbd "C-x k") 'awesome-tab-forward-tab) + +;; 80-charaters mode +(add-hook 'text-mode-hook 'auto-fill-mode) +(setq-default fill-column 80) + +; Global turn on flycheck +(add-hook 'after-init-hook #'global-flycheck-mode) + +; Org Files +(add-hook 'org-mode-hook '(lambda () (setq fill-column 80))) +(add-hook 'org-mode-hook 'auto-fill-mode) +(add-hook 'org-mode-hook 'turn-on-flyspell) + +;; Latex files +(add-hook 'latex-mode-hook 'turn-on-flyspell) +(setq ispell-dictionary "pl") + +;; Broswer +(setq browse-url-browser-function 'browse-url-generic + browse-url-generic-program "browser-x") + +;; Switch-window +(use-package switch-window + :ensure t + :config + (setq + switch-window-increase 4 + switch-window-input-style 'minibuffer + switch-window-shortcut-style 'qwerty + switch-window-threshold 2 + ) + (setq + switch-window-qwerty-shortcuts + '( "a" "s" "d" "f" "g" "h" "j" "k" "l") + ) + :bind + ([remap other-window] . switch-window) + ) + +;; Enable japanese +(if (condition-case nil (require 'mozc)(error nil)) + (setq ecb-be-more-like-better-yes-p t) + (message "Monz not available; not configuring") ) +(setq default-input-method "japanese-mozc") + +;; Enable Smex +(use-package smex + :ensure t + :init + (smex-initialize) + :bind + ("M-x" . smex) + ) + +;; reveal dependency +(use-package htmlize + :ensure t + ) +(use-package ox-reveal + :ensure t + :config + ;; maybe add auto-installer in the future + (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js") +) + +;; moveline +(use-package move-text + :ensure t + :config) + +(global-set-key (kbd "M-") 'move-text-up) +(global-set-key (kbd "M-") 'move-text-down) + +;; company +(use-package company + :ensure t + ) +(global-company-mode) + +;; Shell - bash +(use-package flymake-shellcheck + :ensure t + ) + +(use-package flycheck-bashate + :ensure t + ) + +(require 'bash-completion) +(bash-completion-setup) + +(use-package flymake-shell + :ensure t + ) + +(require 'flymake-shell) +(add-hook 'sh-set-shell-hook 'flymake-shell-load) + +;; Default font +(defun rc/get-default-font () + (cond + ((eq system-type 'windows-nt) "Consolas-13") + ((eq system-type 'gnu/linux) "xos4 Terminus Bold 16"))) + +(add-to-list 'default-frame-alist `(font . ,(rc/get-default-font))) + +;; Theme +(use-package dracula-theme + :ensure t + :config + (load-theme 'dracula t)) + +;; Sitebar dirred +(use-package dired-sidebar + :ensure t + :commands (dired-sidebar-toggle-sidebar)) + +(require 'dired-sidebar) +(global-set-key (kbd "C-x d") 'dired-sidebar-toggle-sidebar) + +;; Smex +(require 'smex) +(smex-initialize) +(global-set-key (kbd "M-x") 'smex) + +;;buffer-move +(require 'buffer-move) +(global-set-key (kbd "") 'buf-move-up) +(global-set-key (kbd "") 'buf-move-down) +(global-set-key (kbd "") 'buf-move-left) +(global-set-key (kbd "") 'buf-move-right) + +;; Multiple-cursors +(use-package multiple-cursors + :ensure t + :config +) + +(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines) +(global-set-key (kbd "C->") 'mc/mark-next-like-this) +(global-set-key (kbd "C-<") 'mc/mark-previous-like-this) +(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this) + +;; Magit +(use-package magit + :ensure t + :config + ) +(global-set-key (kbd "C-x g") 'magit-status) + +;; Helpers for easily building Emacs flymake checkers. +(use-package flymake-easy + :ensure t + :config + ) + +;; Error list +(define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error) +(define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error) + +;; Ruby +(use-package flymake-ruby + :ensure t + :config + ) + +;;robe +(use-package robe + :ensure t + :config + ) + +(require 'robe) +(add-hook 'ruby-mode-hook 'robe-mode) +(add-hook 'robe-mode-hook 'ac-robe-setup) +(eval-after-load 'company + '(push 'company-robe company-backends)) + +(require 'flymake-ruby) +(add-hook 'ruby-mode-hook 'flymake-ruby-load) + +;; Docker +(use-package dockerfile-mode + :ensure t + :defer t) + +;; YAML +(require 'flymake-yaml) + +(use-package yaml-mode + :ensure t + :config + ) + +;; Ansible +(use-package ansible + :ensure t + :config + ) + +(use-package ansible-doc + :ensure t + :config + ) + +(use-package company-ansible + :ensure t + :config + ) + +(add-to-list 'company-backends 'company-ansible) +(add-hook 'yaml-mode-hook '(lambda () (ansible 1))) +(add-hook 'yaml-mode-hook #'ansible-doc-mode) + +;; Markdown-mode +(custom-set-variables + '(markdown-command "/usr/bin/pandoc")) +(use-package markdown-mode + :ensure t + :config + ) + +;; C++ C +(use-package auto-complete-clang + :ensure t + :config + ) + +;; Apache +(use-package apache-mode + :ensure t + :config + ) + +;; Haskell +(use-package haskell-mode + :ensure t + :config +) + +(use-package flycheck-haskell + :ensure t + :config + ) +(add-hook 'flycheck-mode-hook #'flycheck-haskell-setup) + + +;; Java +;; lsp-install-server +;; jdtls +(use-package lsp-java + :ensure t + :config + (global-set-key (kbd "C-.") 'lsp-execute-code-action) +) + +(global-set-key (kbd "") 'dap-breakpoint-toggle) +(global-set-key (kbd "") 'dap-next) +(global-set-key (kbd "") 'dap-step-in) +(global-set-key (kbd "") 'lsp-jt-browser) +(global-set-key (kbd "") 'dap-stop-thread) +(global-set-key (kbd "") 'comment-or-uncomment-region) + +(add-hook 'dap-stopped-hook + (lambda (arg) (call-interactively #'dap-hydra))) + +(setq dap-auto-configure-features '(sessions locals controls tooltip)) + + +(require 'lsp-java) +(add-hook 'java-mode-hook #'lsp) + +(require 'dap-java) + +;; Treemacs +(use-package treemacs + :ensure t + :defer t + :init + (with-eval-after-load 'winum + (define-key winum-keymap (kbd "M-0") #'treemacs-select-window)) + :config + (progn + (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0) + treemacs-deferred-git-apply-delay 0.5 + treemacs-directory-name-transformer #'identity + treemacs-display-in-side-window t + treemacs-eldoc-display t + treemacs-file-event-delay 5000 + treemacs-file-extension-regex treemacs-last-period-regex-value + treemacs-file-follow-delay 0.2 + treemacs-file-name-transformer #'identity + treemacs-follow-after-init t + treemacs-git-command-pipe "" + treemacs-goto-tag-strategy 'refetch-index + treemacs-indentation 2 + treemacs-indentation-string " " + treemacs-is-never-other-window nil + treemacs-max-git-entries 5000 + treemacs-missing-project-action 'ask + treemacs-move-forward-on-expand nil + treemacs-no-png-images nil + treemacs-no-delete-other-windows t + treemacs-project-follow-cleanup nil + treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory) + treemacs-position 'left + treemacs-read-string-input 'from-child-frame + treemacs-recenter-distance 0.1 + treemacs-recenter-after-file-follow nil + treemacs-recenter-after-tag-follow nil + treemacs-recenter-after-project-jump 'always + treemacs-recenter-after-project-expand 'on-distance + treemacs-show-cursor nil + treemacs-show-hidden-files t + treemacs-silent-filewatch nil + treemacs-silent-refresh nil + treemacs-sorting 'alphabetic-asc + treemacs-space-between-root-nodes t + treemacs-tag-follow-cleanup t + treemacs-tag-follow-delay 1.5 + treemacs-user-mode-line-format nil + treemacs-user-header-line-format nil + treemacs-width 35 + treemacs-workspace-switch-cleanup nil) + + ;; The default width and height of the icons is 22 pixels. If you are + ;; using a Hi-DPI display, uncomment this to double the icon size. + ;;(treemacs-resize-icons 44) + + (treemacs-follow-mode t) + (treemacs-filewatch-mode t) + (treemacs-fringe-indicator-mode 'always) + (pcase (cons (not (null (executable-find "git"))) + (not (null treemacs-python-executable))) + (`(t . t) + (treemacs-git-mode 'deferred)) + (`(t . _) + (treemacs-git-mode 'simple)))) + :bind + (:map global-map + ("M-0" . treemacs-select-window) + ("C-x t 1" . treemacs-delete-other-windows) + ("C-x t t" . treemacs) + ("C-x t B" . treemacs-bookmark) + ("C-x t C-t" . treemacs-find-file) + ("C-x t M-t" . treemacs-find-tag))) + +(use-package treemacs-evil + :after treemacs evil + :ensure t) + +(use-package treemacs-projectile + :after treemacs projectile + :ensure t) + +(use-package treemacs-icons-dired + :after treemacs dired + :ensure t + :config (treemacs-icons-dired-mode)) + +(use-package treemacs-magit + :after treemacs magit + :ensure t) + +(use-package treemacs-persp ;;treemacs-perspective if you use perspective.el vs. persp-mode + :after treemacs persp-mode ;;or perspective vs. persp-mode + :ensure t + :config (treemacs-set-scope-type 'Perspectives)) + +;;; --- Look & Feel --- + +;; Helm +(use-package helm + :ensure t + :config +) + +(add-hook 'helm-minibuffer-set-up-hook + 'helm-hide-minibuffer-maybe) + +(setq helm-autoresize-max-height 0) +(setq helm-autoresize-min-height 20) + +(helm-autoresize-mode 1) +(helm-mode 1) + +;; Disable scroll bar +;; no toolbar: +(if (display-graphic-p) + (progn + (tool-bar-mode -1) + (scroll-bar-mode -1))) + +;; Copy +(setq select-active-regions nil) +(setq mouse-drag-copy-region t) +(global-set-key [mouse-2] 'mouse-yank-at-click) + +;; Zoom in/out. +(global-set-key (kbd "M-+") 'text-scale-increase) +(global-set-key (kbd "M--") 'text-scale-decrease) + +;; line numbers: +(global-display-line-numbers-mode 1) + +;; scrolling: +(setq scroll-conservatively 100) + +;; Whitespaces +(global-whitespace-mode 1) +(setq whitespace-display-mappings '((space-mark 32 [?·]))) +(set-face-attribute 'whitespace-space nil :background nil :foreground "gray30") +(setq whitespace-style (quote (face tabs spaces trailing space-before-tab newline indentation empty space-after-tab space-mark tab-mark))) +(add-hook 'before-save-hook (lambda () (delete-trailing-whitespace))) + +;; no "bell" (audible notification): +(setq ring-bell-function 'ignore) + +;; auto reloading (reverting) buffers +(global-auto-revert-mode 1) + +;; disable lock files: +(setq create-lockfiles nil) + +;; disable autosave: +(setq auto-save-default nil) + +;; disable backups: +(setq make-backup-files nil) + +;; Pass "y or n" instead of "yes or no" +(defalias 'yes-or-no-p 'y-or-n-p) + +;; Highlight parens +(show-paren-mode 1) + +;; Candy +(global-prettify-symbols-mode 1) + +;; Modeline +(column-number-mode 1) +(size-indication-mode 1) + +;; Horizontal splitting +(defun split-and-follow-horizontally () + (interactive) + (split-window-below) + (balance-windows) + (other-window 1) + ) +(global-set-key (kbd "C-x 2") 'split-and-follow-horizontally) + +;; Vertical splitting +(defun split-and-follow-vertically () + (interactive) + (split-window-right) + (balance-windows) + (other-window 1) + ) +(global-set-key (kbd "C-x 3") 'split-and-follow-vertically) + +;; Kill & remove split +(defun kill-and-remove-split () + "Kill and remove split." + (interactive) + (kill-buffer) + (delete-window) + (balance-windows) + (other-window 1) + ) +(global-set-key (kbd "C-x x") 'kill-and-remove-split) diff --git a/.emacs.d/plugins/awesome-tab.el b/.emacs.d/plugins/awesome-tab.el new file mode 100644 index 0000000..bb1ab42 --- /dev/null +++ b/.emacs.d/plugins/awesome-tab.el @@ -0,0 +1,1924 @@ +;;; awesome-tab.el --- Provide an out of box configuration to use tab in Emacs. + +;; Filename: awesome-tab.el +;; Description: Provide an out of box configuration to use awesome-tab in Emacs. +;; Author: Andy Stewart +;; Maintainer: Andy Stewart +;; Copyright (C) 2018, Andy Stewart, all rights reserved. +;; Created: 2018-09-17 22:14:34 +;; Version: 7.3 +;; Last-Updated: 2020-04-10 12:36:52 +;; By: Andy Stewart +;; URL: http://www.emacswiki.org/emacs/download/awesome-tab.el +;; Keywords: +;; Compatibility: GNU Emacs 27.0.50 +;; +;; Features that might be required by this library: +;; +;; `cl-lib' `color' `which-func' +;; + +;;; This file is NOT part of GNU Emacs + +;;; License +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; Provide an out of box configuration to use tab in Emacs. +;; + +;;; Installation: +;; +;; Put awesome-tab.el to your load-path. +;; The load-path is usually ~/elisp/. +;; It's set in your ~/.emacs like this: +;; (add-to-list 'load-path (expand-file-name "~/elisp")) +;; +;; And the following to your ~/.emacs startup file. +;; +;; (require 'awesome-tab) +;; (awesome-tab-mode t) +;; +;; No need more. +;; +;; You can use below commands for you need: +;; +;; `awesome-tab-switch-group' +;; `awesome-tab-select-beg-tab' +;; `awesome-tab-select-end-tab' +;; `awesome-tab-forward-tab-other-window' +;; `awesome-tab-backward-tab-other-window' +;; `awesome-tab-kill-other-buffers-in-current-group' +;; `awesome-tab-kill-all-buffers-in-current-group' +;; `awesome-tab-kill-match-buffers-in-current-group' +;; `awesome-tab-keep-match-buffers-in-current-group' +;; `awesome-tab-move-current-tab-to-left' +;; `awesome-tab-move-current-tab-to-right' +;; +;; If you're helm fans, you need add below code in your helm config: +;; +;; (awesome-tab-build-helm-source) +;; + +;;; Customize: +;; +;; `awesome-tab-cycle-scope' +;; `awesome-tab-label-fixed-length' +;; `awesome-tab-auto-scroll-flag' +;; `awesome-tab-common-group-name' +;; `awesometab-hide-tabs-hooks' +;; `awesome-tab-height' +;; `awesome-tab-display-sticky-function-name' +;; `awesome-tab-display-icon' +;; `awesome-tab-show-tab-index' +;; + +;;; Change log: +;; +;; 2020/04/10 +;; * Fix issue #81 +;; +;; 2020/03/22 +;; * Support EAF mode. +;; * Add options for customize active bar. +;; +;; 2020/03/21 +;; * Remove unnecessary tab style and include active bar. +;; +;; 2020/02/13 +;; * Add `awesome-tab-all-the-icons-is-load-p' option. +;; * Fix window-live-p error when click tab. +;; +;; 2020/01/13 +;; * Add new option `awesome-tab-label-max-length'. +;; +;; 2019/12/22 +;; * Add flycheck temp buffer in hide black list. +;; +;; 2019/10/10 +;; * Fix issue #58 click tab select window of tab first. +;; +;; 2019/09/12 +;; * Fix top-line above tab issue. +;; +;; 2019/08/13 +;; * Add new option `awesome-tab-height'. +;; +;; 2019/08/03 +;; * Adjust default value of `awesome-tab-ace-quit-keys'. +;; +;; 2019/08/02 +;; * Refactroy `awesome-tab-ace-jump'. +;; * Refactory variable name. +;; * Remove `awesome-tab-adjust-buffer-order' since `awesome-tab-ace-jump' is enough. +;; +;; 2019/08/01 +;; * Quit when user press Ctrl + g. +;; * Make ace string use foreground same as `font-lock-function-name-face'. +;; * Remove `awesome-tab-prefix-map' +;; +;; 2019/07/18 +;; * Use ema2159's way to render icon. +;; +;; 2019/07/17 +;; * Init `header-line' height from `default' face, +;; `header-line' default inhibit from `mode-line', +;; awesome-tab icon will disappear if `mode-line' height set with 0.1 by other plugins (such as awesome-tray). +;; * Use `stringp' instead `ignore-errors' in `awesome-tab-icon-for-tab'. +;; +;; 2019/07/15 +;; * Don't call `awesome-tab-adjust-buffer-order' if user use mouse click tab. +;; +;; 2019/07/01 +;; * Make awesome-tab's colors change with user selected theme, thank you so much AmaiKinono. +;; * Adjust dark mode background tab's color. +;; * Remove local-mode code. +;; * Refactory code. +;; +;; 2019/06/30 +;; * Add customize option `awesome-tab-display-icon' . +;; +;; 2019/06/28 +;; * Fix messages buffer icon an FontAwesome errors, thanks ema2159. ;) +;; * Set height of tab face, avoid tab render error when user don't load any third theme. +;; * Make `header-line' background same as default face. +;; +;; 2019/06/26 +;; * Fix error of void function awesome-tab-separator-separator-height +;; +;; 2019/06/25 +;; * Still display tab if all-the-icons cause "Error during redisplay" error in MacOS. +;; +;; 2019/06/24 +;; * Use ema2159's patch to fix icon face performance. +;; +;; 2019/06/23 +;; * Render file icon in tab when `all-the-icons' is load. +;; * Use `all-the-icons-icon-for-buffer' to display icon for dired mode. +;; * Support color icon. +;; * Don't customize background of tab, use `default' face's background as tab background. +;; +;; 2019/04/14 +;; * Make `awesome-tab-last-sticky-func-name' default with nil. +;; +;; 2019/03/21 +;; * Make `awesome-tab-last-sticky-func-name' as buffer variable. +;; +;; 2019/03/19 +;; * If `tab-index' more than length of visible tabs, selet the last tab. +;; +;; 2019/03/18 +;; * Add new command `awesome-tab-select-visible-tab'. +;; +;; 2019/03/16 +;; * Fix integerp error. +;; +;; 2019/03/14 +;; * Try to fix numberp error. +;; +;; 2019/03/12 +;; * Display sticky function name in tab. +;; +;; 2019/03/09 +;; * Absorb powerline code, keep single file. +;; * Remove some separator face that not suitable for displaying tab. +;; * Add option `awesome-tab-style'. +;; +;; 2019/03/07 +;; * Add `cl' dependence. +;; +;; 2019/03/03 +;; * Automatically adsorb tabs after switching tabs, making switch tabs quickly. +;; * Fix many typo errors. +;; * Add `awesome-tab-adjust-buffer-order-function'. +;; * Don't trigger by awesome-tab command, it's annoying. +;; +;; 2019/02/23 +;; * Significantly optimize the performance of switching tab by avoiding excessive calls `project-current'. +;; * Use `powerline' render tab, it's beautiful!!! +;; +;; 2018/12/27 +;; * Tab will hide if ```awesome-tab-hide-tab-function``` return t, you can write your own code to customize hide rules. +;; +;; 2018/11/16 +;; * Open new tab on right of current one. +;; +;; 2018/11/14 +;; * Remove wheel features, emacser should only use the keyboard to operate Emacs. +;; +;; 2018/11/01 +;; * Remove `projectile' depend. +;; +;; 2018/10/29 +;; * Add `mwheel' depend. +;; +;; 2018/09/29 +;; * Add new command `awesome-tab-kill-other-buffers-in-current-group' +;; * Not enable mode default. +;; +;; 2018/09/25 +;; * Adjust magit regexp to only match magit buffer, not file that named with magit. +;; +;; 2018/09/22 +;; * Adjust `awesome-tab-buffer-list' to hide unused buffer to user. +;; +;; 2018/09/20 +;; * Remove empty header line from magit buffers. +;; * Add new function `awesome-tab-kill-match-buffers-in-current-group', it's handy in mixin mode, such as web-mode. +;; * Add new function `awesome-tab-keep-match-buffers-in-current-group', it's handy in mixin mode, such as web-mode. +;; * Fix error cause by `awesome-tab-kill-buffer-match-rule'. +;; +;; 2018/09/18 +;; * Fix unselect tab height and add option `awesome-tab-hide-tab-rules' +;; * Use `awesome-tab-groups-hash' store every buffer's project name avoid performance issue cause by `projectile-project-name'. +;; +;; 2018/09/17 +;; * First released. +;; + +;;; Acknowledgements: +;; +;; casouri: documentation and many useful patches. +;; AmaiKinono: contributed to the patch that make tab color change with the theme automatically +;; + +;;; TODO +;; +;; +;; + +;;; Require +(require 'cl-lib) +(require 'color) + +;;; Code: +;;;;;;;;;;;;;;;;;;;;;;; Awesome-Tab source code ;;;;;;;;;;;;;;;;;;;;;;; + +(defgroup awesome-tab nil + "Display a tab bar in the header line." + :group 'convenience) + +(defcustom awesome-tab-cycle-scope nil + "*Specify the scope of cyclic navigation through tabs. +The following scopes are possible: + +- `tabs' + Navigate through visible tabs only. +- `groups' + Navigate through tab groups only. +- default + Navigate through visible tabs, then through tab groups." + :group 'awesome-tab + :type '(choice :tag "Cycle through..." + (const :tag "Visible Tabs Only" tabs) + (const :tag "Tab Groups Only" groups) + (const :tag "Visible Tabs then Tab Groups" nil))) + +(defcustom awesome-tab-auto-scroll-flag t + "*Non-nil means to automatically scroll the tab bar. +That is, when a tab is selected outside of the tab bar visible area, +the tab bar is scrolled horizontally so the selected tab becomes +visible." + :group 'awesome-tab + :type 'boolean) + +(defcustom awesome-tab-common-group-name "Common" + "If the current buffer does not belong to any project, +the group name uses the name of this variable." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-label-fixed-length 0 + "Fixed length of label. Set to 0 if dynamic." + :group 'awesome-tab + :type 'int) + +(defcustom awesome-tab-label-max-length 30 + "Max length of label. Set to 0 if dynamic." + :group 'awesome-tab + :type 'int) + +(defcustom awesometab-hide-tabs-hooks + '(magit-status-mode-hook magit-popup-mode-hook reb-mode-hook) + "Some buffer's header line is empty that make its window insufficient of space to display all content. +Feel free to add hook in this option. ;)" + :type '(repeat symbol) + :group 'awesome-tab) + +(defcustom awesome-tab-display-sticky-function-name nil + "Non-nil to display sticky function name in tab. +Sticky function is the function at the top of the current window sticky." + :group 'awesome-tab + :type 'boolean) + +(defcustom awesome-tab-display-icon t + "Non-nil to display icon in tab, this feature need `all-the-icons' is loaded. +Set this option with nil if you don't like icon in tab." + :group 'awesome-tab + :type 'boolean) + +(defcustom awesome-tab-ace-keys '(?j ?k ?l ?s ?d ?f) + "Keys used for `awesome-tab-ace-jump'." + :group 'awesome-tab + :set #'(lambda (symbol value) + (set-default symbol value) + (let ((1k-seqs nil) + (2k-seqs nil)) + (dolist (a value) + (dolist (b value) + (push (list a b) 2k-seqs)) + (push (list a) 1k-seqs)) + (setq awesome-tab-ace-2-key-seqs (nreverse 2k-seqs)) + (setq awesome-tab-ace-1-key-seqs (nreverse 1k-seqs)))) + :type '(repeat :tag "Keys" character)) + +(defcustom awesome-tab-ace-quit-keys '(?\C-g ?q ?\s) + "Keys used to quit from ace jumping." + :group 'awesome-tab + :type '(repeat :tag "Keys" character)) + +(defcustom awesome-tab-ace-str-style 'replace-icon + "Position of ace strings." + :group 'awesome-tab + :type '(choice + (const :tag "Replace icon" replace-icon) + (const :tag "Left" left) + (const :tag "Right" right))) + +(defcustom awesome-tab-height 190 + "The height of tab face." + :group 'awesome-tab + :type 'int) + +(defcustom awesome-tab-icon-v-adjust 0 + "The v-adjust of tab icon." + :group 'awesome-tab + :type 'float) + +(defcustom awesome-tab-icon-height 0.6 + "The height of icon. +It will render top-line on tab when you set this variable bigger than 0.9." + :group 'awesome-tab + :type 'float) + +(defcustom awesome-tab-show-tab-index nil + "Non-nil to display index in tab." + :group 'awesome-tab + :type 'boolean) + +(defcustom awesome-tab-index-format-str "[%d] " + "Format string for tab index." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-dark-selected-foreground-color nil + "Foreground for selected tab in dark theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-dark-unselected-foreground-color nil + "Foreground for unselected tab in dark theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-light-selected-foreground-color nil + "Foreground for selected tab in light theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-light-unselected-foreground-color nil + "Foreground for unselected tab in light theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-terminal-dark-select-background-color "#222222" + "Select background color for terminal dark mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-dark-select-foreground-color "#AAAAAA" + "Select foreground color for terminal dark mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-dark-unselect-background-color "#000000" + "Unselect background color for terminal dark mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-dark-unselect-foreground-color "#AAAAAA" + "Unselect foreground color for terminal dark mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-light-select-background-color "#AAAAAA" + "Select background color for terminal light mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-light-select-foreground-color "#333333" + "Select foreground color for terminal light mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-light-unselect-background-color "#333333" + "Unselect background color for terminal light mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-terminal-light-unselect-foreground-color "#AAAAAA" + "Unselect foreground color for terminal light mode." + :group 'awesome-tab + :type 'string) + +(defcustom awesome-tab-dark-active-bar-color nil + "The bar color for active tab in dark theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-light-active-bar-color nil + "The bar color for active tab in light theme." + :group 'awesome-tab + :type '(choice (const nil) string)) + +(defcustom awesome-tab-dark-unselected-blend 0.8 + "The blend value for unselected background of dark mode. +Lower value means more contrast." + :group 'awesome-tab + :type 'float) + +(defcustom awesome-tab-light-unselected-blend 0.9 + "The blend value for unselected background of light mode. +Lower value means more contrast." + :group 'awesome-tab + :type 'float) + +(defcustom awesome-tab-active-bar-width 3 + "The width of active bar." + :group 'awesome-tab + :type 'integer) + +(defcustom awesome-tab-active-bar-height 30 + "The height of active bar." + :group 'awesome-tab + :type 'integer) + +(defvar-local awesome-tab-ace-state nil + "Whether current buffer is doing `awesome-tab-ace-jump' or not.") + +(defvar awesome-tab-hide-tab-function 'awesome-tab-hide-tab + "Function to hide tab. +This fucntion accepet tab name, tab will hide if this function return ni.") + +(defvar awesome-tab-current-tabset-function nil + "Function called with no argument to obtain the current tab set. +This is the tab set displayed on the tab bar.") + +(defvar awesome-tab-select-tab-function nil + "Function that select a tab. +The function is passed a tab, and should make it the +selected tab.") + +(defvar awesome-tab-buffer-list-function 'awesome-tab-buffer-list + "Function that returns the list of buffers to show in tabs. +That function is called with no arguments and must return a list of +buffers.") + +(defvar awesome-tab-buffer-groups-function 'awesome-tab-buffer-groups + "Function that gives the group names the current buffer belongs to. +It must return a list of group names, or nil if the buffer has no +group. Notice that it is better that a buffer belongs to one group.") + +(defvar awesome-tab-ace-1-key-seqs nil + "List of 1-key sequences used by `awesome-tab-ace-jump'") + +(defvar awesome-tab-ace-2-key-seqs nil + "List of 2-key sequences used by `awesome-tab-ace-jump'") + +(defvar awesome-tab-all-the-icons-is-load-p (ignore-errors (require 'all-the-icons)) + "Return non-nil if `all-the-icons' is load, `require' will have performance problem, so don't call it dynamically.") + +;;; Misc. +;; +(eval-and-compile + (defalias 'awesome-tab-display-update + (if (fboundp 'force-window-update) + #'(lambda () (force-window-update (selected-window))) + 'force-mode-line-update))) + +(defmacro awesome-tab-kill-buffer-match-rule (match-rule) + `(save-excursion + (mapc #'(lambda (buffer) + (with-current-buffer buffer + (when (string-equal current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))) + (when (funcall ,match-rule buffer) + (kill-buffer buffer)) + ))) + (buffer-list)))) + +;; Copied from s.el +(defun awesome-tab-truncate-string (len s &optional ellipsis) + "If S is longer than LEN, cut it down and add ELLIPSIS to the end. + +The resulting string, including ellipsis, will be LEN characters +long. + +When not specified, ELLIPSIS defaults to ‘...’." + (declare (pure t) (side-effect-free t)) + (unless ellipsis + (setq ellipsis "...")) + (if (> (length s) len) + (format "%s%s" (substring s 0 (- len (length ellipsis))) ellipsis) + (concat s (make-string (- len (length s)) ? )))) + +(defun awesome-tab-refresh-display () + "Refresh the display of tabs. Put this in your user-defined hooks to +make sure the face colors are always right." + (interactive) + (awesome-tab-map-tabsets (lambda (x) (awesome-tab-set-template x nil))) + (awesome-tab-display-update)) + +;;; Tab and tab set +;; +(defsubst awesome-tab-make-tab (object tabset) + "Return a new tab with value OBJECT. +TABSET is the tab set the tab belongs to." + (cons object tabset)) + +(defsubst awesome-tab-tab-value (tab) + "Return the value of tab TAB." + (car tab)) + +(defsubst awesome-tab-tab-tabset (tab) + "Return the tab set TAB belongs to." + (cdr tab)) + +(defvar awesome-tab-tabsets nil + "The tab sets store.") + +(defvar awesome-tab-tabsets-tabset nil + "The special tab set of existing tab sets.") + +(defvar awesome-tab-current-tabset nil + "The tab set currently displayed on the tab bar.") +(make-variable-buffer-local 'awesome-tab-current-tabset) + +(defvar awesome-tab-init-hook nil + "Hook run after tab bar data has been initialized. +You should use this hook to initialize dependent data.") + +(defvar awesome-tab-active-bar nil) + +(defsubst awesome-tab-init-tabsets-store () + "Initialize the tab set store." + (setq awesome-tab-tabsets (make-vector 31 0) + awesome-tab-tabsets-tabset (make-symbol "awesome-tab-tabsets-tabset")) + (put awesome-tab-tabsets-tabset 'start 0) + (run-hooks 'awesome-tab-init-hook)) + +(defvar awesome-tab-quit-hook nil + "Hook run after tab bar data has been freed. +You should use this hook to reset dependent data.") + +(defsubst awesome-tab-free-tabsets-store () + "Free the tab set store." + (setq awesome-tab-tabsets nil + awesome-tab-tabsets-tabset nil) + (run-hooks 'awesome-tab-quit-hook)) + +;; Define an "hygienic" function free of side effect between its local +;; variables and those of the callee. +(eval-and-compile + (defalias 'awesome-tab-map-tabsets + (let ((function (make-symbol "function")) + (result (make-symbol "result")) + (tabset (make-symbol "tabset"))) + `(lambda (,function) + "Apply FUNCTION to each tab set, and make a list of the results. +The result is a list just as long as the number of existing tab sets." + (let (,result) + (mapatoms + #'(lambda (,tabset) + (push (funcall ,function ,tabset) ,result)) + awesome-tab-tabsets) + ,result))))) + +(defun awesome-tab-make-tabset (name &rest objects) + "Make a new tab set whose name is the string NAME. +It is initialized with tabs build from the list of OBJECTS." + (let* ((tabset (intern name awesome-tab-tabsets)) + (tabs (mapcar #'(lambda (object) + (awesome-tab-make-tab object tabset)) + objects))) + (set tabset tabs) + (put tabset 'select (car tabs)) + (put tabset 'start 0) + tabset)) + +(defsubst awesome-tab-get-tabset (name) + "Return the tab set whose name is the string NAME. +Return nil if not found." + (intern-soft name awesome-tab-tabsets)) + +(defsubst awesome-tab-delete-tabset (tabset) + "Delete the tab set TABSET. +That is, remove it from the tab sets store." + (unintern tabset awesome-tab-tabsets)) + +(defsubst awesome-tab-tabs (tabset) + "Return the list of tabs in TABSET." + (symbol-value tabset)) + +(defsubst awesome-tab-tab-values (tabset) + "Return the list of tab values in TABSET." + (mapcar 'awesome-tab-tab-value (awesome-tab-tabs tabset))) + +(defsubst awesome-tab-get-tab (object tabset) + "Search for a tab with value OBJECT in TABSET. +Return the tab found, or nil if not found." + (assoc object (awesome-tab-tabs tabset))) + +(defsubst awesome-tab-member (tab tabset) + "Return non-nil if TAB is in TABSET." + (or (eq (awesome-tab-tab-tabset tab) tabset) + (memq tab (awesome-tab-tabs tabset)))) + +(defsubst awesome-tab-template (tabset) + "Return the cached visual representation of TABSET. +That is, a `header-line-format' template, or nil if the cache is +empty." + (get tabset 'template)) + +(defsubst awesome-tab-set-template (tabset template) + "Set the cached visual representation of TABSET to TEMPLATE. +TEMPLATE must be a valid `header-line-format' template, or nil to +cleanup the cache." + (put tabset 'template template)) + +(defsubst awesome-tab-selected-tab (tabset) + "Return the tab selected in TABSET." + (get tabset 'select)) + +(defsubst awesome-tab-selected-value (tabset) + "Return the value of the tab selected in TABSET." + (awesome-tab-tab-value (awesome-tab-selected-tab tabset))) + +(defsubst awesome-tab-selected-p (tab tabset) + "Return non-nil if TAB is the selected tab in TABSET." + (eq tab (awesome-tab-selected-tab tabset))) + +(defvar awesome-tab--track-selected nil) + +(defsubst awesome-tab-select-tab (tab tabset) + "Make TAB the selected tab in TABSET. +Does nothing if TAB is not found in TABSET. +Return TAB if selected, nil if not." + (when (awesome-tab-member tab tabset) + (unless (awesome-tab-selected-p tab tabset) + (awesome-tab-set-template tabset nil) + (setq awesome-tab--track-selected awesome-tab-auto-scroll-flag)) + (put tabset 'select tab))) + +(defsubst awesome-tab-select-tab-value (object tabset) + "Make the tab with value OBJECT, the selected tab in TABSET. +Does nothing if a tab with value OBJECT is not found in TABSET. +Return the tab selected, or nil if nothing was selected." + (awesome-tab-select-tab (awesome-tab-get-tab object tabset) tabset)) + +(defsubst awesome-tab-start (tabset) + "Return the index of the first visible tab in TABSET." + (get tabset 'start)) + +(defsubst awesome-tab-view (tabset) + "Return the list of visible tabs in TABSET. +That is, the sub-list of tabs starting at the first visible one." + (nthcdr (awesome-tab-start tabset) (awesome-tab-tabs tabset))) + +(defun awesome-tab-add-tab (tabset object) + "Return tab if it has opend. +Otherwise insert new tab on right of current tab." + (let ((tabs (awesome-tab-tabs tabset))) + (if (awesome-tab-get-tab object tabset) + tabs + (let* ((tab (awesome-tab-make-tab object tabset)) + (selected (awesome-tab-selected-tab tabset)) + (selected-index (cl-position (car selected) (mapcar 'car tabs)))) + (awesome-tab-set-template tabset nil) + (set tabset (awesome-tab-insert-at tabs selected-index tab)) + )))) + +(defun awesome-tab-insert-at (list index insert-element) + (let ((counter 0) + (result '())) + (dolist (element list) + (if (equal counter index) + (setq result (append result (list element insert-element))) + (setq result (append result (list element)))) + (setq counter (+ 1 counter))) + result)) + +(defun awesome-tab-delete-tab (tab) + "Remove TAB from its tab set." + (let* ((tabset (awesome-tab-tab-tabset tab)) + (tabs (awesome-tab-tabs tabset)) + (sel (eq tab (awesome-tab-selected-tab tabset))) + (next (and sel (cdr (memq tab tabs))))) + (awesome-tab-set-template tabset nil) + (setq tabs (delq tab tabs)) + ;; When the selected tab is deleted, select the next one, if + ;; available, or the last one otherwise. + (and sel (awesome-tab-select-tab (car (or next (last tabs))) tabset)) + (set tabset tabs))) + +(defun awesome-tab-scroll (tabset count) + "Scroll the visible tabs in TABSET of COUNT units. +If COUNT is positive move the view on right. If COUNT is negative, +move the view on left." + (let ((start (min (max 0 (+ (awesome-tab-start tabset) count)) + (1- (length (awesome-tab-tabs tabset)))))) + (when (/= start (awesome-tab-start tabset)) + (awesome-tab-set-template tabset nil) + (put tabset 'start start)))) + +(defun awesome-tab-tab-next (tabset tab &optional before) + "Search in TABSET for the tab after TAB. +If optional argument BEFORE is non-nil, search for the tab before +TAB. Return the tab found, or nil otherwise." + (let* (last (tabs (awesome-tab-tabs tabset))) + (while (and tabs (not (eq tab (car tabs)))) + (setq last (car tabs) + tabs (cdr tabs))) + (and tabs (if before last (nth 1 tabs))))) + +(defun awesome-tab-current-tabset (&optional update) + "Return the tab set currently displayed on the tab bar. +If optional argument UPDATE is non-nil, call the user defined function +`awesome-tab-current-tabset-function' to obtain it. Otherwise return the +current cached copy." + (and update awesome-tab-current-tabset-function + (setq awesome-tab-current-tabset + (funcall awesome-tab-current-tabset-function))) + awesome-tab-current-tabset) + +(defun awesome-tab-get-tabsets-tabset () + "Return the tab set of selected tabs in existing tab sets." + (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab)) + (awesome-tab-scroll awesome-tab-tabsets-tabset 0) + (awesome-tab-set-template awesome-tab-tabsets-tabset nil) + awesome-tab-tabsets-tabset) + +;;; Faces +;; + +(defface awesome-tab-unselected-face + '((t)) + "Face used for unselected tabs. +Do not customize this. It's attribute will be calculated on the +fly to fit your theme." + :group 'awesome-tab) + +(defface awesome-tab-selected-face + '((t)) + "Face used for the selected tab. +Do not customize this. It's attribute will be calculated on the +fly to fit your theme." + :group 'awesome-tab) + +(defface awesome-tab-unselected-ace-face + '((t (:inherit 'font-lock-function-name-face))) + "Face used for ace string on unselected tabs. +Note that the background will be calculated on the fly, so +customize the background will not have any effect." + :group 'awesome-tab) + +(defface awesome-tab-selected-ace-face + '((t (:inherit 'font-lock-function-name-face))) + "Face used for ace string on selected tabs. +Note that the background will be calculated on the fly, so +customize the background will not have any effect." + :group 'awesome-tab) + +(defface awesome-tab-unselected-index-face + '((t (:inherit 'font-lock-function-name-face))) + "Face used for index on unselected tabs. +Note that the background will be calculated on the fly, so +customize the background will not have any effect." + :group 'awesome-tab) + +(defface awesome-tab-selected-index-face + '((t (:inherit 'font-lock-function-name-face))) + "Face used for index on selected tabs. +Note that the background will be calculated on the fly, so +customize the background will not have any effect." + :group 'awesome-tab) + +;;; Tabs +;; +(defun awesome-tab-make-header-line-mouse-map (mouse function) + (let ((map (make-sparse-keymap))) + (define-key map (vector 'header-line mouse) function) + map)) + +(defun awesome-tab-color-blend (c1 c2 alpha) + "Blend two colors C1 and C2 with ALPHA. +C1 and C2 are hexidecimal strings. +ALPHA is a number between 0.0 and 1.0 which corresponds to the +influence of C1 on the result." + (apply #'(lambda (r g b) + (format "#%02x%02x%02x" + (ash r -8) + (ash g -8) + (ash b -8))) + (cl-mapcar + (lambda (x y) + (round (+ (* x alpha) (* y (- 1 alpha))))) + (color-values c1) (color-values c2)))) + +(defun awesome-tab-adjust-color-with-theme () + "We need adjust awesome-tab's colors when user switch new theme." + (let* ((bg-mode (frame-parameter nil 'background-mode)) + (select-tab-background (awesome-tab-get-select-background-color)) + (unselect-tab-background (awesome-tab-get-unslect-background-color))) + (when (display-graphic-p) + (setq awesome-tab-active-bar (awesome-tab-make-xpm awesome-tab-active-bar-width awesome-tab-active-bar-height))) + + (set-face-attribute 'header-line nil :height awesome-tab-height) + + (set-face-attribute 'awesome-tab-selected-face nil + :foreground (awesome-tab-get-select-foreground-color) + :distant-foreground (awesome-tab-get-select-foreground-color) + :background select-tab-background) + (set-face-attribute 'awesome-tab-unselected-face nil + :foreground (awesome-tab-get-unselect-foreground-color) + :distant-foreground (awesome-tab-get-unselect-foreground-color) + :background unselect-tab-background) + + (set-face-attribute 'awesome-tab-selected-ace-face nil + :background select-tab-background) + (set-face-attribute 'awesome-tab-unselected-ace-face nil + :background unselect-tab-background) + + (set-face-attribute 'awesome-tab-selected-index-face nil + :background select-tab-background) + (set-face-attribute 'awesome-tab-unselected-index-face nil + :background unselect-tab-background))) + +(defun awesome-tab-get-select-foreground-color () + (let ((bg-mode (frame-parameter nil 'background-mode))) + (if (display-graphic-p) + (cond ((eq bg-mode 'dark) (or awesome-tab-dark-selected-foreground-color + (face-foreground 'font-lock-doc-face))) + ((eq bg-mode 'light) (or awesome-tab-light-selected-foreground-color + (face-foreground 'font-lock-doc-face)))) + (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-select-foreground-color) + ((eq bg-mode 'light) awesome-tab-terminal-light-select-foreground-color)) + ))) + +(defun awesome-tab-get-unselect-foreground-color () + (let ((bg-mode (frame-parameter nil 'background-mode))) + (if (display-graphic-p) + (cond ((eq bg-mode 'dark) (or awesome-tab-dark-unselected-foreground-color + (face-foreground 'font-lock-comment-face))) + ((eq bg-mode 'light) (or awesome-tab-light-unselected-foreground-color + (face-foreground 'font-lock-comment-face)))) + (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-unselect-foreground-color) + ((eq bg-mode 'light) awesome-tab-terminal-light-unselect-foreground-color)) + ))) + +(defun awesome-tab-get-select-background-color () + (let ((bg-mode (frame-parameter nil 'background-mode))) + (if (display-graphic-p) + (face-background 'default) + (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-select-background-color) + ((eq bg-mode 'light) awesome-tab-terminal-light-select-background-color)) + ))) + +(defun awesome-tab-get-unslect-background-color () + (let* ((bg-mode (frame-parameter nil 'background-mode))) + (if (display-graphic-p) + (cond ((eq bg-mode 'dark) + (awesome-tab-color-blend (face-background 'default) "#000000" awesome-tab-dark-unselected-blend)) + ((eq bg-mode 'light) + (awesome-tab-color-blend (face-background 'default) "#000000" awesome-tab-light-unselected-blend))) + (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-unselect-background-color) + ((eq bg-mode 'light) awesome-tab-terminal-light-unselect-background-color)) + ))) + +(defun awesome-tab-line-format (tabset) + "Return the `header-line-format' value to display TABSET." + ;; Adjust color with theme. + (awesome-tab-adjust-color-with-theme) + ;; Reder tab line. + (let* ((sel (awesome-tab-selected-tab tabset)) + (tabs (awesome-tab-view tabset)) + (bg-mode (frame-parameter nil 'background-mode)) + (header-line-color (awesome-tab-get-unslect-background-color)) + atsel elts) + ;; Track the selected tab to ensure it is always visible. + (when awesome-tab--track-selected + (while (not (memq sel tabs)) + (awesome-tab-scroll tabset -1) + (setq tabs (awesome-tab-view tabset))) + (while (and tabs (not atsel)) + (setq elts (cons (awesome-tab-line-tab (car tabs)) elts) + atsel (eq (car tabs) sel) + tabs (cdr tabs))) + (setq elts (nreverse elts)) + ;; At this point the selected tab is the last elt in ELTS. + ;; Scroll TABSET and ELTS until the selected tab becomes + ;; visible. + (let (buffer-list-update-hook) + (with-temp-buffer + (let ((truncate-partial-width-windows nil) + (inhibit-modification-hooks t) + deactivate-mark ;; Prevent deactivation of the mark! + start) + (setq truncate-lines nil + buffer-undo-list t) + (setq start (point)) + (while (and (cdr elts) ;; Always show the selected tab! + (progn + (delete-region start (point-max)) + (goto-char (point-max)) + (apply 'insert elts) + (goto-char (point-min)) + (> (vertical-motion 1) 0))) + (awesome-tab-scroll tabset 1) + (setq elts (cdr elts)))))) + (setq elts (nreverse elts)) + (setq awesome-tab--track-selected nil)) + ;; Format remaining tabs. + (while tabs + (setq elts (cons (awesome-tab-line-tab (car tabs)) elts) + tabs (cdr tabs))) + ;; Cache and return the new tab bar. + (awesome-tab-set-template + tabset + (list (nreverse elts) + (propertize "%-" + 'face (list :background header-line-color + :foreground header-line-color + :distant-foreground header-line-color) + 'pointer 'arrow))))) + +(defun awesome-tab-line () + "Return the header line templates that represent the tab bar. +Inhibit display of the tab bar in current window `awesome-tab-hide-tab-function' return nil." + (cond + ((awesome-tab-hide-tab-cached (current-buffer)) + ;; Don't show the tab bar. + (setq header-line-format nil)) + ((awesome-tab-current-tabset t) + ;; When available, use a cached tab bar value, else recompute it. + (or (awesome-tab-template awesome-tab-current-tabset) + (awesome-tab-line-format awesome-tab-current-tabset))))) + +(defconst awesome-tab-header-line-format '(:eval (awesome-tab-line)) + "The tab bar header line format.") + +;;; Cyclic navigation through tabs +;; +(defun awesome-tab-cycle (&optional backward type) + "Cycle to the next available tab. +The scope of the cyclic navigation through tabs is specified by the +option `awesome-tab-cycle-scope'. +If optional argument BACKWARD is non-nil, cycle to the previous tab +instead." + (let* ((tabset (awesome-tab-current-tabset t)) + (ttabset (awesome-tab-get-tabsets-tabset)) + ;; If navigation through groups is requested, and there is + ;; only one group, navigate through visible tabs. + (cycle (if (and (eq awesome-tab-cycle-scope 'groups) + (not (cdr (awesome-tab-tabs ttabset)))) + 'tabs + awesome-tab-cycle-scope)) + selected tab) + (when tabset + (setq selected (awesome-tab-selected-tab tabset)) + (cond + ;; Cycle through visible tabs only. + ((eq cycle 'tabs) + (setq tab (awesome-tab-tab-next tabset selected backward)) + ;; When there is no tab after/before the selected one, cycle + ;; to the first/last visible tab. + (unless tab + (setq tabset (awesome-tab-tabs tabset) + tab (car (if backward (last tabset) tabset)))) + ) + ;; Cycle through tab groups only. + ((eq cycle 'groups) + (setq tab (awesome-tab-tab-next ttabset selected backward)) + ;; When there is no group after/before the selected one, cycle + ;; to the first/last available group. + (unless tab + (setq tabset (awesome-tab-tabs ttabset) + tab (car (if backward (last tabset) tabset)))) + ) + (t + ;; Cycle through visible tabs then tab groups. + (setq tab (awesome-tab-tab-next tabset selected backward)) + ;; When there is no visible tab after/before the selected one, + ;; cycle to the next/previous available group. + (unless tab + (setq tab (awesome-tab-tab-next ttabset selected backward)) + ;; When there is no next/previous group, cycle to the + ;; first/last available group. + (unless tab + (setq tabset (awesome-tab-tabs ttabset) + tab (car (if backward (last tabset) tabset)))) + ;; Select the first/last visible tab of the new group. + (setq tabset (awesome-tab-tabs (awesome-tab-tab-tabset tab)) + tab (car (if backward (last tabset) tabset)))) + )) + (awesome-tab-buffer-select-tab tab)))) + +;;;###autoload +(defun awesome-tab-backward () + "Select the previous available tab. +Depend on the setting of the option `awesome-tab-cycle-scope'." + (interactive) + (awesome-tab-cycle t)) + +;;;###autoload +(defun awesome-tab-forward () + "Select the next available tab. +Depend on the setting of the option `awesome-tab-cycle-scope'." + (interactive) + (awesome-tab-cycle)) + +;;;###autoload +(defun awesome-tab-backward-group () + "Go to selected tab in the previous available group." + (interactive) + (let ((awesome-tab-cycle-scope 'groups)) + (awesome-tab-cycle t))) + +;;;###autoload +(defun awesome-tab-forward-group () + "Go to selected tab in the next available group." + (interactive) + (let ((awesome-tab-cycle-scope 'groups)) + (awesome-tab-cycle))) + +;;;###autoload +(defun awesome-tab-backward-tab () + "Select the previous visible tab." + (interactive) + (let ((awesome-tab-cycle-scope 'tabs)) + (awesome-tab-cycle t))) + +;;;###autoload +(defun awesome-tab-forward-tab () + "Select the next visible tab." + (interactive) + (let ((awesome-tab-cycle-scope 'tabs)) + (awesome-tab-cycle))) + +;;; Minor modes +;; +(defsubst awesome-tab-mode-on-p () + "Return non-nil if Awesome-Tab mode is on." + (eq (default-value 'header-line-format) + awesome-tab-header-line-format)) + +;;; Awesome-Tab mode +;; +(defvar awesome-tab-mode-map + (let ((km (make-sparse-keymap))) + km) + "Keymap to use in Awesome-Tab mode.") + +(defvar awesome-tab--global-hlf nil) + +;;;###autoload +(define-minor-mode awesome-tab-mode + "Toggle display of a tab bar in the header line. +With prefix argument ARG, turn on if positive, otherwise off. +Returns non-nil if the new state is enabled. + +\\{awesome-tab-mode-map}" + :group 'awesome-tab + :require 'awesome-tab + :global t + :keymap awesome-tab-mode-map + (if awesome-tab-mode +;;; ON + (unless (awesome-tab-mode-on-p) + ;; Save current default value of `header-line-format'. + (setq awesome-tab--global-hlf (default-value 'header-line-format)) + (awesome-tab-init-tabsets-store) + (setq-default header-line-format awesome-tab-header-line-format)) +;;; OFF + (when (awesome-tab-mode-on-p) + ;; Restore previous `header-line-format'. + (setq-default header-line-format awesome-tab--global-hlf) + (awesome-tab-free-tabsets-store)) + )) + +;;; Buffer tabs +;; +(defgroup awesome-tab-buffer nil + "Display buffers in the tab bar." + :group 'awesome-tab) + +(defun awesome-tab-filter (condp lst) + (delq nil + (mapcar (lambda (x) (and (funcall condp x) x)) lst))) + +(defun awesome-tab-filter-out (condp lst) + (delq nil + (mapcar (lambda (x) (if (funcall condp x) nil x)) lst))) + +(defun awesome-tab-buffer-list () + "Return the list of buffers to show in tabs. +Exclude buffers whose name starts with a space, when they are not +visiting a file. The current buffer is always included." + (awesome-tab-filter-out + 'awesome-tab-hide-tab-cached + (delq nil + (mapcar #'(lambda (b) + (cond + ;; Always include the current buffer. + ((eq (current-buffer) b) b) + ((buffer-file-name b) b) + ((char-equal ?\ (aref (buffer-name b) 0)) nil) + ((buffer-live-p b) b))) + (buffer-list))))) + +(defun awesome-tab-buffer-mode-derived-p (mode parents) + "Return non-nil if MODE derives from a mode in PARENTS." + (let (derived) + (while (and (not derived) mode) + (if (memq mode parents) + (setq derived t) + (setq mode (get mode 'derived-mode-parent)))) + derived)) + +;;; Group buffers in tab sets. +;; +(defvar awesome-tab--buffers nil) + +(defun awesome-tab-buffer-update-groups () + "Update tab sets from groups of existing buffers. +Return the the first group where the current buffer is." + (let ((bl (sort + (mapcar + #'(lambda (b) + (with-current-buffer b + (list (current-buffer) + (buffer-name) + (if awesome-tab-buffer-groups-function + (funcall awesome-tab-buffer-groups-function) + '(awesome-tab-common-group-name))))) + (and awesome-tab-buffer-list-function + (funcall awesome-tab-buffer-list-function))) + #'(lambda (e1 e2) + (string-lessp (nth 1 e1) (nth 1 e2)))))) + ;; If the cache has changed, update the tab sets. + (unless (equal bl awesome-tab--buffers) + ;; Add new buffers, or update changed ones. + (dolist (e bl) + (dolist (g (nth 2 e)) + (let ((tabset (awesome-tab-get-tabset g))) + (if tabset + (unless (equal e (assq (car e) awesome-tab--buffers)) + ;; This is a new buffer, or a previously existing + ;; buffer that has been renamed, or moved to another + ;; group. Update the tab set, and the display. + (awesome-tab-add-tab tabset (car e)) + (awesome-tab-set-template tabset nil)) + (awesome-tab-make-tabset g (car e)))))) + ;; Remove tabs for buffers not found in cache or moved to other + ;; groups, and remove empty tabsets. + (mapc 'awesome-tab-delete-tabset + (awesome-tab-map-tabsets + #'(lambda (tabset) + (dolist (tab (awesome-tab-tabs tabset)) + (let ((e (assq (awesome-tab-tab-value tab) bl))) + (or (and e (memq tabset + (mapcar 'awesome-tab-get-tabset + (nth 2 e)))) + (awesome-tab-delete-tab tab)))) + ;; Return empty tab sets + (unless (awesome-tab-tabs tabset) + tabset)))) + ;; The new cache becomes the current one. + (setq awesome-tab--buffers bl))) + ;; Return the first group the current buffer belongs to. + (car (nth 2 (assq (current-buffer) awesome-tab--buffers)))) + +;;; Tab bar callbacks +;; +(defvar awesome-tab--buffer-show-groups nil) + +(defsubst awesome-tab-buffer-show-groups (flag) + "Set display of tabs for groups of buffers to FLAG." + (setq awesome-tab--buffer-show-groups flag)) + +(defun awesome-tab-buffer-tabs () + "Return the buffers to display on the tab bar, in a tab set." + (let ((tabset (awesome-tab-get-tabset (awesome-tab-buffer-update-groups)))) + (awesome-tab-select-tab-value (current-buffer) tabset) + (when awesome-tab--buffer-show-groups + (setq tabset (awesome-tab-get-tabsets-tabset)) + (awesome-tab-select-tab-value (current-buffer) tabset)) + tabset)) + +(defsubst awesome-tab-line-tab (tab) + "Return the display representation of tab TAB. +That is, a propertized string used as an `header-line-format' template +element." + (propertize + (awesome-tab-buffer-tab-label tab) + 'pointer 'hand + 'local-map (purecopy (awesome-tab-make-header-line-mouse-map + 'mouse-1 + `(lambda (event) (interactive "e") + (let ((tab-window (window-at (cadr (mouse-position)) + (cddr (mouse-position)) + (car (mouse-position))))) + (when tab-window + (select-window tab-window) + (awesome-tab-buffer-select-tab ',tab)))))))) + +(defun awesome-tab-buffer-tab-label (tab) + "Return a label for TAB. +That is, a string used to represent it on the tab bar." + (let* ((is-active-tab (awesome-tab-selected-p tab (awesome-tab-current-tabset))) + (tab-face (if is-active-tab 'awesome-tab-selected-face 'awesome-tab-unselected-face)) + (index-face (if is-active-tab 'awesome-tab-selected-index-face 'awesome-tab-unselected-index-face)) + (current-buffer-index (cl-position tab (awesome-tab-view (awesome-tab-current-tabset t)))) + (ace-str-face (if is-active-tab 'awesome-tab-selected-ace-face 'awesome-tab-unselected-ace-face)) + (current-buffer-index (cl-position tab (awesome-tab-view awesome-tab-current-tabset))) + (ace-str (if awesome-tab-ace-state (elt ace-strs current-buffer-index) "")) + (ace-state awesome-tab-ace-state)) + (concat + ;; Active bar. + (when (and is-active-tab awesome-tab-active-bar) + (propertize awesome-tab-active-bar)) + ;; Ace string. + (when (and ace-state (eq awesome-tab-ace-str-style 'left)) + (propertize ace-str 'face ace-str-face)) + ;; Tab icon. + (when (and awesome-tab-display-icon + awesome-tab-all-the-icons-is-load-p) + (propertize " " 'face tab-face)) + (if (and ace-state (eq awesome-tab-ace-str-style 'replace-icon)) + (propertize ace-str 'face ace-str-face) + (awesome-tab-icon-for-tab tab tab-face)) + ;; Tab label. + (propertize (awesome-tab-tab-name tab) 'face tab-face) + ;; Ace string. + (when (and ace-state (eq awesome-tab-ace-str-style 'right)) + (propertize ace-str 'face ace-str-face)) + ;; Tab index. + (when awesome-tab-show-tab-index + (propertize (format awesome-tab-index-format-str (+ current-buffer-index 1)) 'face index-face)) + ))) + +(defun awesome-tab-make-xpm (width height) + "Create an XPM bitmap via WIDTH and HEIGHT." + (when (and (display-graphic-p) + (image-type-available-p 'xpm)) + (propertize + " " 'display + (let ((data (make-list height (make-list width 1))) + (color (pcase (frame-parameter nil 'background-mode) + ('dark (or awesome-tab-dark-active-bar-color + (face-background 'highlight))) + ('light (or awesome-tab-light-active-bar-color + (face-background 'highlight)))))) + (ignore-errors + (create-image + (concat + (format + "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," + (length (car data)) (length data) color color) + (apply #'concat + (cl-loop with idx = 0 + with len = (length data) + for dl in data + do (cl-incf idx) + collect + (concat + "\"" + (cl-loop for d in dl + if (= d 0) collect (string-to-char " ") + else collect (string-to-char ".")) + (if (eq idx len) "\"};" "\",\n"))))) + 'xpm t :ascent 'center)))))) + +(defun awesome-tab-tab-name (tab) + "Render tab's name. +Tab name will truncate if option `awesome-tab-truncate-string' big than zero." + (format " %s " + (let ((bufname (awesome-tab-buffer-name (car tab)))) + (cond ((> awesome-tab-label-fixed-length 0) + (awesome-tab-truncate-string awesome-tab-label-fixed-length bufname)) + ((> awesome-tab-label-max-length 0) + (let ((ellipsis "...")) + (if (> (length bufname) awesome-tab-label-max-length) + (format "%s%s" (substring bufname 0 (- awesome-tab-label-max-length (length ellipsis))) ellipsis) + bufname))) + (t + bufname)) + ))) + +(defun awesome-tab-icon-for-tab (tab face) + "When tab buffer's file is exists, use `all-the-icons-icon-for-file' to fetch file icon. +Otherwise use `all-the-icons-icon-for-buffer' to fetch icon for buffer." + (when (and awesome-tab-display-icon + awesome-tab-all-the-icons-is-load-p) + (let* ((tab-buffer (car tab)) + (tab-file (buffer-file-name tab-buffer)) + (background (face-background face)) + (icon + (cond + ;; Use `all-the-icons-icon-for-file' if current file is exists. + ((and + tab-file + (file-exists-p tab-file)) + (all-the-icons-icon-for-file tab-file :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height)) + ;; Use `all-the-icons-icon-for-mode' for current tab buffer at last. + (t + (with-current-buffer tab-buffer + (if (derived-mode-p tab-buffer 'eaf-mode) + (all-the-icons-faicon "html5" :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height) + (all-the-icons-icon-for-mode major-mode :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height)) + ))))) + (when (and icon + ;; `get-text-property' need icon is string type. + (stringp icon)) + ;; Thanks ema2159 for code block ;) + (propertize icon 'face `(:inherit ,(get-text-property 0 'face icon) :background ,background)))))) + +(defun awesome-tab-buffer-name (tab-buffer) + "Get buffer name of tab. +Will merge sticky function name in tab if option `awesome-tab-display-sticky-function-name' is non-nil." + (if (and awesome-tab-display-sticky-function-name + (boundp 'awesome-tab-last-sticky-func-name) + awesome-tab-last-sticky-func-name + (equal tab-buffer (current-buffer))) + (format "%s [%s]" (buffer-name tab-buffer) awesome-tab-last-sticky-func-name) + (buffer-name tab-buffer))) + +(defvar awesome-tab-last-scroll-y 0 + "Holds the scroll y of window from the last run of post-command-hooks.") + +(defun awesome-tab-monitor-window-scroll () + "This function is used to monitor the window scroll. +Currently, this function is only use for option `awesome-tab-display-sticky-function-name'." + (when awesome-tab-display-sticky-function-name + (let ((scroll-y (window-start))) + (when (and scroll-y + (integerp scroll-y)) + (unless (equal scroll-y awesome-tab-last-scroll-y) + (let ((func-name (save-excursion + (goto-char scroll-y) + (require 'which-func) + (which-function)))) + (when (or + (not (boundp 'awesome-tab-last-sticky-func-name)) + (not (equal func-name awesome-tab-last-sticky-func-name))) + (set (make-local-variable 'awesome-tab-last-sticky-func-name) func-name) + + ;; Use `ignore-errors' avoid integerp error when execute `awesome-tab-line-format'. + (ignore-errors + (awesome-tab-line-format awesome-tab-current-tabset)) + )))) + (setq awesome-tab-last-scroll-y scroll-y)))) + +(add-hook 'post-command-hook 'awesome-tab-monitor-window-scroll) + +(defun awesome-tab-buffer-select-tab (tab) + "Select tab." + (let ((buffer (awesome-tab-tab-value tab))) + (switch-to-buffer buffer) + (awesome-tab-buffer-show-groups nil) + (awesome-tab-display-update) + )) + +(defun awesome-tab-buffer-track-killed () + "Hook run just before actually killing a buffer. +In Awesome-Tab mode, try to switch to a buffer in the current tab bar, +after the current buffer has been killed. Try first the buffer in tab +after the current one, then the buffer in tab before. On success, put +the sibling buffer in front of the buffer list, so it will be selected +first." + (and (eq header-line-format awesome-tab-header-line-format) + (eq awesome-tab-current-tabset-function 'awesome-tab-buffer-tabs) + (eq (current-buffer) (window-buffer (selected-window))) + (let ((bl (awesome-tab-tab-values (awesome-tab-current-tabset))) + (b (current-buffer)) + found sibling) + (while (and bl (not found)) + (if (eq b (car bl)) + (setq found t) + (setq sibling (car bl))) + (setq bl (cdr bl))) + (when (and (setq sibling (or (car bl) sibling)) + (buffer-live-p sibling)) + ;; Move sibling buffer in front of the buffer list. + (save-current-buffer + (switch-to-buffer sibling)))))) + +;;; Tab bar buffer setup +;; +(defun awesome-tab-buffer-init () + "Initialize tab bar buffer data. +Run as `awesome-tab-init-hook'." + (setq awesome-tab--buffers nil + awesome-tab--buffer-show-groups nil + awesome-tab-current-tabset-function 'awesome-tab-buffer-tabs + awesome-tab-select-tab-function 'awesome-tab-buffer-select-tab + ) + (add-hook 'kill-buffer-hook 'awesome-tab-buffer-track-killed)) + +(defun awesome-tab-buffer-quit () + "Quit tab bar buffer. +Run as `awesome-tab-quit-hook'." + (setq awesome-tab--buffers nil + awesome-tab--buffer-show-groups nil + awesome-tab-current-tabset-function nil + awesome-tab-select-tab-function nil + ) + (remove-hook 'kill-buffer-hook 'awesome-tab-buffer-track-killed)) + +(add-hook 'awesome-tab-init-hook 'awesome-tab-buffer-init) +(add-hook 'awesome-tab-quit-hook 'awesome-tab-buffer-quit) + +;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;; +(defun awesome-tab-switch-group (&optional groupname) + "Switch tab groups using ido." + (interactive) + (let* ((tab-buffer-list (mapcar + #'(lambda (b) + (with-current-buffer b + (list (current-buffer) + (buffer-name) + (funcall awesome-tab-buffer-groups-function) ))) + (funcall awesome-tab-buffer-list-function))) + (groups (awesome-tab-get-groups)) + (group-name (or groupname + (completing-read "Switch to group: " + groups nil t))) ) + (catch 'done + (mapc + #'(lambda (group) + (when (equal group-name (car (car (cdr (cdr group))))) + (throw 'done (switch-to-buffer (car (cdr group)))))) + tab-buffer-list) ))) + +(defun awesome-tab-select-end-tab () + "Select end tab of current tabset." + (interactive) + (awesome-tab-select-beg-tab t)) + +(defun awesome-tab-select-beg-tab (&optional backward type) + "Select beginning tab of current tabs. +If BACKWARD is non-nil, move backward, otherwise move forward. +TYPE is default option." + (interactive) + (let* ((tabset (awesome-tab-current-tabset t)) + (ttabset (awesome-tab-get-tabsets-tabset)) + (cycle (if (and (eq awesome-tab-cycle-scope 'groups) + (not (cdr (awesome-tab-tabs ttabset)))) + 'tabs + awesome-tab-cycle-scope)) + selected tab) + (when tabset + (setq selected (awesome-tab-selected-tab tabset)) + (setq tabset (awesome-tab-tabs tabset) + tab (car (if backward (last tabset) tabset))) + (awesome-tab-buffer-select-tab tab)))) + +(defun awesome-tab-backward-tab-other-window (&optional reversed) + "Move to left tab in other window. +Optional argument REVERSED default is move backward, if reversed is non-nil move forward." + (interactive) + (other-window 1) + (if reversed + (awesome-tab-forward-tab) + (awesome-tab-backward-tab)) + (other-window -1)) + +(defun awesome-tab-forward-tab-other-window () + "Move to right tab in other window." + (interactive) + (awesome-tab-backward-tab-other-window t)) + +(defun awesome-tab-move-current-tab-to-right () + "Move current tab one place right, unless it's already the rightmost." + (interactive) + (let* ((bufset (awesome-tab-current-tabset t)) + (old-bufs (awesome-tab-tabs bufset)) + (first-buf (car old-bufs)) + (new-bufs (list))) + (while (and + old-bufs + (not (string= (buffer-name) (format "%s" (car (car old-bufs)))))) + (push (car old-bufs) new-bufs) + (setq old-bufs (cdr old-bufs))) + (if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing + (progn + (setq the-buffer (car old-bufs)) + (setq old-bufs (cdr old-bufs)) + (if old-bufs ; if this is false, then the current tab is the rightmost + (push (car old-bufs) new-bufs)) + (push the-buffer new-bufs)) ; this is the tab that was to be moved + (error "Error: current buffer's name was not found in Awesome-Tab's buffer list.")) + (setq new-bufs (reverse new-bufs)) + (setq new-bufs (append new-bufs (cdr old-bufs))) + (set bufset new-bufs) + (awesome-tab-set-template bufset nil) + (awesome-tab-display-update))) + +(defun awesome-tab-move-current-tab-to-left () + "Move current tab one place left, unless it's already the leftmost." + (interactive) + (let* ((bufset (awesome-tab-current-tabset t)) + (old-bufs (awesome-tab-tabs bufset)) + (first-buf (car old-bufs)) + (new-bufs (list))) + (if (string= (buffer-name) (format "%s" (car first-buf))) + old-bufs ; the current tab is the leftmost + (setq not-yet-this-buf first-buf) + (setq old-bufs (cdr old-bufs)) + (while (and + old-bufs + (not (string= (buffer-name) (format "%s" (car (car old-bufs)))))) + (push not-yet-this-buf new-bufs) + (setq not-yet-this-buf (car old-bufs)) + (setq old-bufs (cdr old-bufs))) + (if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing + (progn + (push (car old-bufs) new-bufs) ; this is the tab that was to be moved + (push not-yet-this-buf new-bufs) + (setq new-bufs (reverse new-bufs)) + (setq new-bufs (append new-bufs (cdr old-bufs)))) + (error "Error: current buffer's name was not found in Awesome-Tab's buffer list.")) + (set bufset new-bufs) + (awesome-tab-set-template bufset nil) + (awesome-tab-display-update)))) + +(defun awesome-tab-move-current-tab-to-beg () + "Move current tab to the first position." + (interactive) + (let* ((bufset (awesome-tab-current-tabset t)) + (bufs (copy-sequence (awesome-tab-tabs bufset))) + (current-tab-index + (cl-position (current-buffer) (mapcar #'car bufs))) + (current-tab (elt bufs current-tab-index))) + (setq bufs (delete current-tab bufs)) + (push current-tab bufs) + (set bufset bufs) + (awesome-tab-set-template bufset nil) + (awesome-tab-display-update))) + +(defun awesome-tab-kill-all-buffers-in-current-group () + "Kill all buffers in current group." + (interactive) + (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))) + ;; Kill all buffers in current group. + (awesome-tab-kill-buffer-match-rule + (lambda (buffer) t)) + ;; Switch to next group. + (awesome-tab-forward-group) + )) + +(defun awesome-tab-kill-other-buffers-in-current-group () + "Kill all buffers except current buffer in current group." + (interactive) + (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))) + (currentbuffer (current-buffer))) + ;; Kill all buffers in current group. + (awesome-tab-kill-buffer-match-rule + (lambda (buffer) (not (equal buffer currentbuffer)))) + )) + +(defun awesome-tab-kill-match-buffers-in-current-group () + "Kill all buffers match extension in current group." + (interactive) + (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))) + (extension-names (awesome-tab-get-extensions)) + match-extension) + ;; Read extension need to kill. + (setq match-extension (completing-read "Kill buffers suffix with: " + extension-names nil t)) + ;; Kill all buffers match extension in current group. + (awesome-tab-kill-buffer-match-rule + (lambda (buffer) + (let ((filename (buffer-file-name buffer))) + (and filename (string-equal (file-name-extension filename) match-extension)) + ))) + ;; Switch to next group if last file killed. + (when (equal (length extension-names) 1) + (awesome-tab-forward-group)) + )) + +(defun awesome-tab-keep-match-buffers-in-current-group () + "Keep all buffers match extension in current group." + (interactive) + (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))) + (extension-names (awesome-tab-get-extensions)) + match-extension) + ;; Read extension need to kill. + (setq match-extension (completing-read "Keep buffers suffix with: " + extension-names nil t)) + ;; Kill all buffers match extension in current group. + (awesome-tab-kill-buffer-match-rule + (lambda (buffer) + (let ((filename (buffer-file-name buffer))) + (and filename (not (string-equal (file-name-extension filename) match-extension))) + ))))) + +(defun awesome-tab-select-visible-nth-tab (tab-index) + "Select visible tab with `tab-index'. +Example, when `tab-index' is 1, this function will select the leftmost label in the visible area, +instead of the first label in the current group. + +If `tab-index' more than length of visible tabs, selet the last tab. + +If `tab-index' is 0, select last tab." + (let ((visible-tabs (awesome-tab-view awesome-tab-current-tabset))) + (switch-to-buffer + (car + (if (or (equal tab-index 0) + (> tab-index (length visible-tabs))) + (car (last visible-tabs)) + (nth (- tab-index 1) visible-tabs)))))) + +(defun awesome-tab-select-visible-tab () + "Bind this function with number keystroke, such as s-1, s-2, s-3 ... etc. + +This function automatically recognizes the number at the end of the keystroke +and switches to the tab of the corresponding index. + +Note that this function switches to the visible range, +not the actual logical index position of the current group." + (interactive) + (let* ((event last-command-event) + (key (make-vector 1 event)) + (key-desc (key-description key)) + (tab-index (string-to-number + ;; Fix issue #81 that `last-command-event' is not keystroke. + (cond ((equal (length key-desc) 1) + key-desc) + (t + (nth 1 (split-string key-desc "-"))))))) + (awesome-tab-select-visible-nth-tab tab-index))) + +(defun awesome-tab-build-ace-strs (len key-number seqs) + "Build strings for `awesome-tab-ace-jump'. +LEN is the number of strings, should be the number of current visible +tabs. NKEYS should be 1 or 2." + (let ((i 0) + (str nil)) + (when (>= key-number 3) + (error "NKEYS should be 1 or 2")) + (while (< i len) + (push (apply #'string (elt seqs i)) str) + (setq i (1+ i))) + (nreverse str))) + +(defun awesome-tab-open-tab-in-current-group () + "Open a tab in current group." + (interactive) + (let* ((generate-candidate (lambda (tab) + (propertize + (string-trim (awesome-tab-tab-name tab)) + 'awesome-tab-tab-entity tab))) + (candidates (mapcar generate-candidate + (awesome-tab-tabs (awesome-tab-current-tabset))))) + (awesome-tab-buffer-select-tab + (get-text-property 0 'awesome-tab-tab-entity + (completing-read "Open tab:" candidates nil t))))) + +(defun awesome-tab-ace-jump () + "Jump to a visible tab by 1 or 2 chars." + (interactive) + (catch 'quit + (let* ((visible-tabs (awesome-tab-view awesome-tab-current-tabset)) + (visible-tabs-length (length visible-tabs)) + done-flag + (lower-bound 0) + (upper-bound visible-tabs-length) + (ace-keys (length awesome-tab-ace-keys)) + (key-number (cond + ((<= visible-tabs-length ace-keys) 1) + ((<= visible-tabs-length (* ace-keys ace-keys)) 2) + (t (error "Too many visible tabs. Put more keys into `awesome-tab-ace-keys'.")))) + (visible-seqs + (cl-subseq + (symbol-value + (intern + (concat "awesome-tab-ace-" (number-to-string key-number) "-key-seqs"))) + 0 visible-tabs-length)) + (ace-strs (awesome-tab-build-ace-strs visible-tabs-length key-number visible-seqs))) + (setq awesome-tab-ace-state t) + (awesome-tab-refresh-display) + (dotimes (i key-number) + (while (not done-flag) + (let ((char (with-local-quit (read-key (format "Awesome Tab Ace Jump (%d):" (1+ i)))))) + (if (not (member char awesome-tab-ace-quit-keys)) + (let ((current-chars (mapcar #'car visible-seqs))) + (when (member char current-chars) + (setq done-flag t) + (setq lower-bound (cl-position char current-chars)) + (setq upper-bound (1- (- visible-tabs-length (cl-position char (nreverse current-chars))))) + (dotimes (lower-index lower-bound) + (setcar (nthcdr lower-index visible-seqs) nil)) + (setq upper-index (1+ upper-bound)) + (while (< upper-index visible-tabs-length) + (setcar (nthcdr upper-index visible-seqs) nil) + (setq upper-index (1+ upper-index))) + (setq upper-index 0) + )) + ;; Quit when user press Ctrl + g. + (setq awesome-tab-ace-state nil) + (awesome-tab-refresh-display) + (throw 'quit nil)))) + (setq done-flag nil) + (setq visible-seqs (mapcar #'cdr visible-seqs)) + (setq ace-strs (awesome-tab-build-ace-strs visible-tabs-length key-number visible-seqs)) + (awesome-tab-refresh-display)) + (setq awesome-tab-ace-state nil) + (awesome-tab-refresh-display) + (awesome-tab-buffer-select-tab (nth lower-bound visible-tabs))))) + +;;;;;;;;;;;;;;;;;;;;;;; Utils functions ;;;;;;;;;;;;;;;;;;;;;;; +(defun awesome-tab-get-groups () + ;; Refresh groups. + (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab)) + (mapcar #'(lambda (group) + (format "%s" (cdr group))) + (awesome-tab-tabs awesome-tab-tabsets-tabset))) + +(defun awesome-tab-get-extensions () + ;; Refresh groups. + (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab)) + (let ((extension-names '())) + (mapc #'(lambda (buffer) + (with-current-buffer buffer + (when (string-equal current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))) + (when (buffer-file-name buffer) + (add-to-list 'extension-names (file-name-extension (buffer-file-name buffer)))) + ))) + (buffer-list)) + extension-names)) + +;;;;;;;;;;;;;;;;;;;;;;; Default configurations ;;;;;;;;;;;;;;;;;;;;;;; + +;; Uniquify tab name when open multiple buffers with same filename. +(setq uniquify-separator "/") +(setq uniquify-buffer-name-style 'post-forward-angle-brackets) +(setq uniquify-after-kill-buffer-p t) + +(dolist (hook awesometab-hide-tabs-hooks) + (add-hook hook '(lambda () (setq-local header-line-format nil)))) + +;; Rules to control buffer's group rules. +(defvar awesome-tab-groups-hash (make-hash-table :test 'equal)) +(defvar awesome-tab-hide-hash (make-hash-table :test 'equal)) + +(defun awesome-tab-project-name () + (let ((project-name (cdr (project-current)))) + (if project-name + (format "Project: %s" (expand-file-name project-name)) + awesome-tab-common-group-name))) + +(defun awesome-tab-get-group-name (buf) + (let ((group-name (gethash buf awesome-tab-groups-hash))) + ;; Return group name cache if it exists for improve performance. + (if group-name + group-name + ;; Otherwise try get group name with `project-current'. + ;; `project-current' is very slow, it will slow down Emacs if you call it when switch buffer. + (with-current-buffer buf + (let ((project-name (awesome-tab-project-name))) + (puthash buf project-name awesome-tab-groups-hash) + project-name))))) + +(defun awesome-tab-buffer-groups () + "`awesome-tab-buffer-groups' control buffers' group rules. + +Group awesome-tab with mode if buffer is derived from `eshell-mode' `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'. +All buffer name start with * will group to \"Emacs\". +Other buffer group by `awesome-tab-get-group-name' with project name." + (list + (cond + ((or (string-equal "*" (substring (buffer-name) 0 1)) + (memq major-mode '(magit-process-mode + magit-status-mode + magit-diff-mode + magit-log-mode + magit-file-mode + magit-blob-mode + magit-blame-mode + ))) + "Emacs") + ((derived-mode-p 'eshell-mode) + "EShell") + ((derived-mode-p 'emacs-lisp-mode) + "Elisp") + ((derived-mode-p 'dired-mode) + "Dired") + ((memq major-mode '(org-mode org-agenda-mode diary-mode)) + "OrgMode") + ((derived-mode-p 'eaf-mode) + "EAF") + (t + (awesome-tab-get-group-name (current-buffer)))))) + +;; Helm source for switching group in helm. +(defvar helm-source-awesome-tab-group nil) + +(defun awesome-tab-build-helm-source () + (interactive) + (setq helm-source-awesome-tab-group + (when (ignore-errors (require 'helm)) + (helm-build-sync-source "Awesome-Tab Group" + :candidates #'awesome-tab-get-groups + :action '(("Switch to group" . awesome-tab-switch-group)))))) + +;;;###autoload +(defun awesome-tab-counsel-switch-group () + "Switch group of awesome-tab." + (interactive) + (when (ignore-errors (require 'ivy)) + (ivy-read + "Awesome-Tab Groups:" + (awesome-tab-get-groups) + :action #'awesome-tab-switch-group + :caller 'awesome-tab-counsel-switch-group))) + +(defun awesome-tab-hide-tab (x) + (let ((name (format "%s" x))) + (or + ;; Current window is not dedicated window. + (window-dedicated-p (selected-window)) + + ;; Buffer name not match below blacklist. + (string-prefix-p "*epc" name) + (string-prefix-p "*helm" name) + (string-prefix-p "*Compile-Log*" name) + (string-prefix-p "*lsp" name) + (string-prefix-p "*flycheck" name) + + ;; Is not magit buffer. + (and (string-prefix-p "magit" name) + (not (file-name-extension name))) + ))) + +(defun awesome-tab-hide-tab-cached (buf) + (let ((hide (gethash buf awesome-tab-hide-hash 'not-found))) + (when (eq hide 'not-found) + (setq hide (funcall awesome-tab-hide-tab-function buf)) + (puthash buf hide awesome-tab-hide-hash)) + hide)) + +(defvar awesome-tab-last-focus-buffer nil + "The last focus buffer.") + +(defvar awesome-tab-last-focus-buffer-group nil + "The group name of last focus buffer.") + +(defun awesome-tab-remove-nth-element (nth list) + (if (zerop nth) (cdr list) + (let ((last (nthcdr (1- nth) list))) + (setcdr last (cddr last)) + list))) + +(defun awesome-tab-insert-after (list aft-el el) + "Insert EL after AFT-EL in LIST." + (push el (cdr (member aft-el list))) + list) + +(defun awesome-tab-insert-before (list bef-el el) + "Insert EL before BEF-EL in LIST." + (nreverse (awesome-tab-insert-after (nreverse list) bef-el el))) + +(advice-add 'load-theme :around #'awesome-tab-load-theme) +(defun awesome-tab-load-theme (orig-fun &optional arg &rest args) + "Update tab after execute `load-theme'." + (apply orig-fun arg args) + (awesome-tab-refresh-display)) + +(provide 'awesome-tab) + +;;; awesome-tab.el ends here diff --git a/.emacs.d/plugins/bash-completion.el b/.emacs.d/plugins/bash-completion.el new file mode 100644 index 0000000..5c391b9 --- /dev/null +++ b/.emacs.d/plugins/bash-completion.el @@ -0,0 +1,1723 @@ +;;; bash-completion.el --- BASH completion for the shell buffer -*- lexical-binding: t -*- + +;; Copyright (C) 2009 Stephane Zermatten + +;; Author: Stephane Zermatten +;; Maintainer: Stephane Zermatten +;; Version: 3.1.0 +;; Keywords: shell bash bash-completion +;; URL: http://github.com/szermatt/emacs-bash-completion +;; Package-Requires: ((emacs "24.3")) + +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2 of the +;; License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; `http://www.gnu.org/licenses/'. + +;;; Commentary: +;; +;; This file defines dynamic completion hooks for shell-mode and +;; shell-command prompts that are based on bash completion. +;; +;; Bash completion for emacs: +;; - is aware of bash builtins, aliases and functions +;; - does file expansion inside of colon-separated variables +;; and after redirections (> or <) +;; - escapes special characters when expanding file names +;; - is configurable through programmable bash completion +;; +;; When the first completion is requested in shell model or a shell +;; command, bash-completion.el starts a separate bash +;; process. Bash-completion.el then uses this process to do the actual +;; completion and includes it into Emacs completion suggestions. +;; +;; A simpler and more complete alternative to bash-completion.el is to +;; run a bash shell in a buffer in term mode(M-x `ansi-term'). +;; Unfortunately, many Emacs editing features are not available when +;; running in term mode. Also, term mode is not available in +;; shell-command prompts. +;; +;; Bash completion can also be run programmatically, outside of a +;; shell-mode command, by calling +;; `bash-completion-dynamic-complete-nocomint' +;; +;; INSTALLATION +;; +;; 1. copy bash-completion.el into a directory that's on Emacs load-path +;; 2. add this into your .emacs file: +;; (autoload 'bash-completion-dynamic-complete \"bash-completion\" +;; \"BASH completion hook\") +;; (add-hook 'shell-dynamic-complete-functions +;; 'bash-completion-dynamic-complete) +;; +;; or simpler, but forces you to load this file at startup: +;; +;; (require 'bash-completion) +;; (bash-completion-setup) +;; +;; 3. reload your .emacs (M-x `eval-buffer') or restart +;; +;; Once this is done, use as usual to do dynamic completion from +;; shell mode or a shell command minibuffer, such as the one started +;; for M-x `compile'. Note that the first completion is slow, as emacs +;; launches a new bash process. +;; +;; Naturally, you'll get better results if you turn on programmable +;; bash completion in your shell. Depending on how your system is set +;; up, this might requires calling: +;; . /etc/bash_completion +;; from your ~/.bashrc. +;; +;; When called from a bash shell buffer, +;; `bash-completion-dynamic-complete' communicates with the current shell +;; to reproduce, as closely as possible the normal bash auto-completion, +;; available on full terminals. +;; +;; When called from non-shell buffers, such as the prompt of M-x +;; compile, `bash-completion-dynamic-complete' creates a separate bash +;; process just for doing completion. Such processes have the +;; environment variable EMACS_BASH_COMPLETE set to t, to help +;; distinguish them from normal shell processes. +;; +;; See the documentation of the function +;; `bash-completion-dynamic-complete-nocomint' to do bash completion +;; from other buffers or completion engines. +;; +;; COMPATIBILITY +;; +;; bash-completion.el is known to work with Bash 3, 4 and 5, on Emacs, +;; starting with version 24.3, under Linux and OSX. It does not work +;; on XEmacs. +;; + +;;; History: +;; +;; Full history is available on +;; https://github.com/szermatt/emacs-bash-completion + +;;; Code: + +(require 'comint) +(require 'cl-lib) +(require 'shell) + +;;; ---------- Customization +(defgroup bash-completion nil + "BASH configurable command-line completion " + :group 'shell + :group 'shell-command) + +(defcustom bash-completion-enabled t + "Enable/Disable BASH configurable command-line completion globally. + +This flag is useful for temporarily disabling bash completion +once it's been installed. + +Setting this variable to t is NOT enough to enable BASH completion. +BASH completion is only available in the environment for which +`bash-completion-dynamic-complete' has been registered. See +`bash-completion-setup' for that." + :type '(boolean) + :group 'bash-completion) + +(defcustom bash-completion-use-separate-processes nil + "Enable/disable the use of separate processes to perform completion. + +When set to a non-nil value, separate processes will always be +used to perform completion. If nil, process associated with the +current buffer will be used to perform completion from a shell +buffer associated to a bash shell, and otherwise a separate process +will be started to do completion." + :type 'boolean + :group 'bash-completion) + +(defcustom bash-completion-prog (executable-find "bash") + "Name or path of the BASH executable to run for command-line completion. + +This should be either an absolute path to the BASH executable or +the name of the bash command if it is on Emacs' PATH. This should +point to a recent version of BASH, 3 or 4, with support for +command-line completion. + +This variable is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation." + :type '(file :must-match t) + :group 'bash-completion) + +(defcustom bash-completion-remote-prog "bash" + "Name or path of the remote BASH executable to use. + +This is the path of an BASH executable available on the remote machine. +Best is to just specify \"bash\" and rely on the PATH being set correctly +for the remote connection. + +This variable is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation." + :type '(string) + :group 'bash-completion) + +(defcustom bash-completion-args '("--noediting") + "Args passed to the BASH shell. + +This variable is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation." + :type '(repeat (string :tag "Argument")) + :group 'bash-completion) + +(defcustom bash-completion-process-timeout 2.5 + "Number of seconds to wait for an answer from bash. + +If bash takes longer than that to answer, the answer will be +ignored." + :type '(float) + :group 'bash-completion) + +(defcustom bash-completion-command-timeout 30 + "Number of seconds to wait for an answer from programmable +completion functions. + +Programmable completion functions might take an arbitrary long +time to run, so this should be long." + :type '(float) + :group 'bash-completion) + +(defcustom bash-completion-message-delay 0.4 + "Time to wait before displaying a message while waiting for results. + +If completion takes longer than that time, a message is displayed +on the minibuffer to make it clear what's happening. Set to nil +to never display any such message. 0 to always display it. + +Only relevant when using bash completion in a shell, through +`bash-completion-dynamic-complete'." + :type '(float) + :group 'bash-completion) + +(defcustom bash-completion-initial-timeout 30 + "Timeout value to apply when talking to bash for the first time. + +The first thing bash is supposed to do is process /etc/bash_complete, +which typically takes a long time. + +This variable is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation." + :type '(float) + :group 'bash-completion) + +(defcustom bash-completion-nospace nil + "Never let bash add a final space at the end of a completion. + +When there is only one completion candidate, bash sometimes adds +a space at the end of the completion to move the cursor at the +appropriate position to add more command-line arguments. This +feature doesn't always work perfectly with programmable completion. + +Enable this option if you find yourself having to often backtrack +to remove the extra space bash adds after a completion." + :type '(boolean) + :group 'bash-completion) + +(defvar bash-completion-start-files + '("~/.emacs_bash.sh" "~/.emacs.d/init_bash.sh") + "Shell files that, if they exist, will be sourced at the beginning of a bash completion subprocess. + +This variable is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation.") + +(defvar bash-completion-wordbreaks "" + "Extra wordbreaks to use when tokenizing, in `bash-completion-tokenize'.") + +(defvar bash-completion-output-buffer " *bash-completion*" + "Buffer storing completion results. + +This buffer is only used when creating separate processes for +performing completion. See +`bash-completion-use-separate-processes' for further +explanation.") + +;;; ---------- Internal variables and constants + +(defvar bash-completion-processes nil + "Bash processes alist. + +Mapping between remote paths as returned by `file-remote-p' and +Bash processes.") + +(defconst bash-completion-special-chars "[ -$&-*,:-<>?[-^`{-}]" + "Regexp of characters that must be escaped or quoted.") + +(defconst bash-completion--ps1 "'\t$?\v'" + "Value for the special PS1 prompt set for completions, quoted.") + +(eval-when-compile + (unless (or (and (= emacs-major-version 24) (>= emacs-minor-version 3)) + (>= emacs-major-version 25)) + (error + (concat + "Emacs version 24.3 or later is required to run emacs-bash-completion.\n" + "Download emacs-bash-completion version 2.1 to run on older Emacs " + "versions, from 22 to 24.")))) + +(defvar bash-completion--debug-info nil + "Alist that stores info about the last call to `bash-completion-send'. + +Created by `bash-completion-send' and printed by +`bash-completion-debug'.") + +;;; ---------- Struct + +;; The main, completion structure, keeping track of the definition and +;; state of the current completion. +(cl-defstruct (completion (:constructor bash-completion--make) + (:conc-name bash-completion--) + (:copier nil)) + line ; the relevant command (string) + words ; line split into words, unescaped (list of strings) + cword ; 0-based index of the word to be completed in words (number) + unparsed-stub ; unparsed version of the thing we are completing, + ; that is, the part of the last word after the last + ; wordbreak separator. + stub-start ; start position of the thing we are completing + stub ; parsed version of the stub + open-quote ; quote open at stub end: nil, ?' or ?\"" + compgen-args ; compgen arguments for this command (list of strings) + wordbreaks ; value of COMP_WORDBREAKS active for this completion + compopt ; options forced with compopt nil or `(nospace . ,bool) +) + +(defun bash-completion--type (comp) + "Returns the type of COMP. + +Completion type is 'command, if completing a command (cword = 0), +'custom if there's a custom completion for the current command or +'default if there isn't or if the completion hasn't been +customized, usually by `bash-completion--customize'. +" + (cond + ((zerop (bash-completion--cword comp)) 'command) + ((bash-completion--compgen-args comp) 'custom) + (t 'default))) + +(defun bash-completion--nospace (comp) + "Returns the value of the nospace option to use for COMP. + +The option can be: + - set globally, by setting `bash-completion-nospace' to t + - set for a customized completion, in bash, with + '-o' 'nospace'." + (let ((cell)) + (cond + (bash-completion-nospace t) ; set globally + ((setq cell (assq 'nospace (bash-completion--compopt comp))) + (cdr cell)) + (t (bash-completion--has-compgen-option + (bash-completion--compgen-args comp) "nospace"))))) + +(defun bash-completion--command (comp) + "Return the current command for the completion, if there is one." + (file-name-nondirectory (car (bash-completion--words comp)))) + +(defun bash-completion--get-buffer (process) + "Return the buffer used to store completion results. + +PROCESS is the bash process from which completions are +retrieved. When `bash-completion-use-separate-processes' is nil, +PROCESS is not used and `bash-completion-output-buffer' is +returned." + (if bash-completion-use-separate-processes + (process-buffer process) + (get-buffer-create bash-completion-output-buffer))) + +(defun bash-completion--setup-bash-common (process) + "Setup PROCESS to be ready for completion." + (let (bash-major-version) + (bash-completion-send "complete -p" process) + (process-put process 'complete-p + (bash-completion-build-alist (bash-completion--get-buffer process))) + (bash-completion-send "echo -n ${BASH_VERSINFO[0]}" process) + (setq bash-major-version + (with-current-buffer (bash-completion--get-buffer process) + (string-to-number (buffer-substring-no-properties + (point-min) (point-max))))) + (bash-completion-send + (concat "function __emacs_complete_wrapper {" + (if (>= bash-major-version 4) + " COMP_TYPE=9; COMP_KEY=9; _EMACS_COMPOPT=\"\";" + "") + " eval $__EMACS_COMPLETE_WRAPPER;" + " n=$?;" + " if [[ $n = 124 ]]; then" + (bash-completion--side-channel-data "wrapped-status" "124") + " return 1; " + " fi; " + (when (>= bash-major-version 4) + (concat + " if [[ -n \"${_EMACS_COMPOPT}\" ]]; then" + (bash-completion--side-channel-data "compopt" "${_EMACS_COMPOPT}") + " fi;")) + " return $n;" + "}") + process) + (if (>= bash-major-version 4) + (bash-completion-send + (concat + "function compopt {" + " command compopt \"$@\" 2>/dev/null;" + " ret=$?; " + " if [[ $ret == 1 && \"$*\" = *\"-o nospace\"* ]]; then" + " _EMACS_COMPOPT='-o nospace';" + " return 0;" + " fi;" + " if [[ $ret == 1 && \"$*\" = *\"+o nospace\"* ]]; then" + " _EMACS_COMPOPT='+o nospace';" + " return 0;" + " fi;" + " return $ret; " + "}") + process)) + + ;; some bash completion functions use quote_readline + ;; to double-quote strings - which compgen understands + ;; but only in some environment. disable this dreadful + ;; business to get a saner way of handling spaces. + ;; Noticed in bash_completion v1.872. + (bash-completion-send "function quote_readline { echo \"$1\"; }" process) + + (bash-completion-send "echo -n ${COMP_WORDBREAKS}" process) + (process-put process 'wordbreaks + (with-current-buffer (bash-completion--get-buffer process) + (buffer-substring-no-properties + (point-min) (point-max)))) + (process-put process 'bash-major-version bash-major-version) + + (bash-completion-send "bind -v 2>/dev/null" process) + (process-put process 'completion-ignore-case + (with-current-buffer (bash-completion--get-buffer process) + (save-excursion + (goto-char (point-min)) + (and (search-forward "completion-ignore-case on" nil 'noerror) t)))) + + (process-put process 'setup-done t))) + +;;; ---------- Inline functions + +(defsubst bash-completion-tokenize-get-range (token) + "Return the TOKEN range as a cons: (start . end)." + (cdr (assq 'range token))) + +(defsubst bash-completion-tokenize-set-end (token) + "Set the end position of TOKEN to the cursor position." + (setcdr (bash-completion-tokenize-get-range token) (point))) + +(defsubst bash-completion-tokenize-append-str (token str) + "Append to TOKEN the string STR." + (let ((str-cons (assq 'str token))) + (setcdr str-cons (concat (cdr str-cons) str)))) + +(defsubst bash-completion-tokenize-get-str (token) + "Return the TOKEN string." + (cdr (assq 'str token))) + +(defsubst bash-completion-tokenize-open-quote (tokens) + "Return the quote character that was still open in the last token. + +TOKENS is a list of token as returned by +`bash-completion-tokenize'." + (cdr (assq 'quote (car (last tokens))))) + +;;; ---------- Functions: completion + +;;;###autoload +(defun bash-completion-setup () + "Register bash completion for the shell buffer and shell command line. + +This function adds `bash-completion-dynamic-complete' to the completion +function list of shell mode, `shell-dynamic-complete-functions'. + +This function is convenient, but it might not be the best way of enabling +bash completion in your .emacs file because it forces you to load the module +before it is needed. For an autoload version, add: + + (autoload 'bash-completion-dynamic-complete \"bash-completion\" + \"BASH completion hook\") + (add-hook 'shell-dynamic-complete-functions + 'bash-completion-dynamic-complete) +" + (add-hook 'shell-dynamic-complete-functions + 'bash-completion-dynamic-complete)) + +;;;###autoload +(defun bash-completion-dynamic-complete () + "Return the completion table for bash command at point. + +This function is meant to be added into +`shell-dynamic-complete-functions'. It uses `comint' to figure +out what the current command is and returns a completion table or +nil if no completions available. + +When doing completion outside of a comint buffer, call +`bash-completion-dynamic-complete-nocomint' instead." + (let ((message-timer + (if (and (not (window-minibuffer-p)) + (not (null bash-completion-message-delay))) + (run-at-time + bash-completion-message-delay nil + (lambda () (message "Bash completion...")))))) + (unwind-protect + (bash-completion-dynamic-complete-nocomint + (comint-line-beginning-position) + (point) + 'dynamic-table) + ;; cleanup + (if message-timer + (cancel-timer message-timer))))) + +;;;###autoload +(defun bash-completion-dynamic-complete-nocomint + (comp-start &optional comp-pos dynamic-table) + "Return completion information for bash command at an arbitrary position. + +The bash command to be completed begins at COMP-START in the +current buffer. This must specify where the current command +starts, usually right after the prompt. + +COMP-POS is the point where completion should happen, usually +just (point). Note that a bash command can span across multiple +line, so COMP-START is not necessarily on the same line as +COMP-POS. + +This function does not assume that the current buffer is a shell +or even comint buffer. It can safely be called from any buffer +where a bash command appears, including `completion-at-point'. + +If DYNAMIC-TABLE is passed a non-nil value, the resulting +collection will be a function that fetches the result lazily, +when it's called. + +When calling from `completion-at-point', make sure to pass a +non-nil value to DYNAMIC-TABLE. This isn't just an optimization: +returning a function instead of a list tells Emacs it should +avoids post-filtering the results and possibly discarding useful +completion from bash. + +When calling from another completion engine, make sure to treat +the returned completion as reliable and not post-process them +further. + +Returns (list stub-start stub-end completions) with + - stub-start, the position at which the completed region starts + - stub-end, the position at which the completed region ends + - completions, a possibly empty list of completion candidates + or a function, if DYNAMIC-TABLE is non-nil, a lambda such as the one + returned by `completion-table-dynamic'" + (when bash-completion-enabled + (let ((comp-start (or comp-start (line-beginning-position))) + (comp-pos (or comp-pos (point))) + (bash-completion-use-separate-processes + bash-completion-use-separate-processes) + (process (bash-completion--get-process))) + (when (and (not process) (not bash-completion-use-separate-processes)) + ;; no process associated with the current buffer, create a + ;; separate completion process + (setq bash-completion-use-separate-processes t) + (setq process (bash-completion--get-process))) + (let* ((comp (bash-completion--parse + comp-start comp-pos + (process-get process 'wordbreaks) + (process-get process 'bash-major-version))) + (stub-start (bash-completion--stub-start comp))) + + (bash-completion--customize comp process) + (list + stub-start + comp-pos + (if dynamic-table + (bash-completion--completion-table-with-cache + comp process) + (bash-completion-comm comp process))))))) + +(defun bash-completion--find-last (elt array) + "Return the position of the last instance of ELT in array or nil." + (catch 'bash-completion-return + (let ((array-len (length array))) + (dotimes (index array-len) + (if (eq elt (aref array (- array-len index 1))) + (throw 'bash-completion-return (- array-len index 1))))) + nil)) + +;;; ---------- Functions: parsing and tokenizing + +(defun bash-completion-join (words) + "Join WORDS into a shell command line. + +All words that contain even mildly suspicious characters are +quoted using single quotes to avoid the shell interpreting them +when it shouldn't. + +Return one string containing WORDS." + (if words + (mapconcat + 'bash-completion-quote + words " ") + "")) + +(defun bash-completion-quote (word) + "Put single quotes around WORD unless it's crearly unnecessary. + +If WORD contains characters that aren't known to be harmless, this +functions adds single quotes around it and return the result." + (cond + ((string= "" word) + "''") + ((string-match "^[a-zA-Z0-9_./-]*$" word) + word) + (t + (concat "'" + (replace-regexp-in-string "'" "'\\''" word nil t) + "'")))) + +(defun bash-completion--parse (comp-start comp-pos wordbreaks bash-major-version) + "Process a command from COMP-START to COMP-POS. + +WORDBREAK is the value of COMP_WORDBREAKS to use for this completion, +usually taken from the current process. + +Returns a completion struct." + (let* ((all-tokens (bash-completion-tokenize + comp-start comp-pos (if (>= bash-major-version 4) + wordbreaks ""))) + (line-tokens (bash-completion-parse-current-command all-tokens)) + (first-token (car line-tokens)) + (last-token (car (last line-tokens))) + (open-quote (bash-completion-tokenize-open-quote line-tokens)) + (start (or (car (bash-completion-tokenize-get-range first-token)) comp-pos)) + (end (or (cdr (bash-completion-tokenize-get-range last-token)) comp-pos)) + (words (bash-completion-strings-from-tokens line-tokens)) + (rebuilt-line) (stub-start) (unparsed-stub) (parsed-stub)) + ;; Note about rebuilt-line: When using readline, line and words + ;; would be passed unquoted to the functions. This doesn't work, + ;; however, when called from Emacs as when readline 'compgen -f' + ;; behaves differently and does not unquote the string it's + ;; passed. This is why words and the last word of the line are + ;; passed unquoted. This makes the standard bash completion + ;; scripts work - possibly at the cost of more inconsistencies + ;; with other scripts. + (if (or (> comp-pos end) (= start end)) + (setq stub-start comp-pos + unparsed-stub "" + parsed-stub "" + words (append words '("")) + rebuilt-line (buffer-substring-no-properties start comp-pos)) + (if (< bash-major-version 4) + (setq last-token (car (last (bash-completion-tokenize + start comp-pos wordbreaks))))) + (setq stub-start (car (bash-completion-tokenize-get-range last-token)) + parsed-stub (bash-completion-tokenize-get-str last-token) + unparsed-stub (buffer-substring-no-properties stub-start comp-pos) + rebuilt-line (concat + (buffer-substring-no-properties + start (car (cdr (assq 'range (car (last line-tokens)))))) + (cdr (assq 'str (car (last line-tokens))))))) + (bash-completion--make + :line rebuilt-line + :cword (- (length words) 1) + :words words + :stub-start stub-start + :unparsed-stub unparsed-stub + :stub parsed-stub + :open-quote open-quote + :wordbreaks wordbreaks))) + +(defun bash-completion-parse-current-command (tokens) + "Extract from TOKENS the tokens forming the current command. + +This function takes a list of TOKENS created by +`bash-completion-tokenize' for the current buffer and select the +tokens on this list that form the current command given that the +word to be completed is the last token. + +For example, given this stream of tokens: + cd /var/tmp && ls -l +if the last token is -l, it will select: + ls -l +if the last token is /var/tmp, it will select: + cd /var/tmp + +Return a sublist of TOKENS." + (nreverse + (catch 'bash-completion-return + (let ((command nil) (state 'initial)) + (dolist (token tokens) + (let* ((string (bash-completion-tokenize-get-str token)) + (is-terminal + (and (member string '(";" "&" "|" "&&" "||" "\n")) + (let ((range (bash-completion-tokenize-get-range token))) + (= (- (cdr range) (car range)) + (length string)))))) + (cond + (is-terminal + (setq state 'initial) + (setq command nil)) + + ((and (eq state 'initial) + (null (string-match "=" string))) + (setq state 'args) + (push token command)) + + ((eq state 'args) + (push token command))))) + (or command (last tokens)))))) + +(defun bash-completion-strings-from-tokens (tokens) + "Extract the strings from TOKENS. + +This function takes all strings from TOKENS and return it as a +list of strings. + +TOKENS should be in the format returned by `bash-completion-tokenize'." + (mapcar 'bash-completion-tokenize-get-str tokens)) + +(defun bash-completion-tokenize (start end &optional wordbreaks) + "Tokenize the portion of the current buffer between START and END. + +This function splits a BASH command line into tokens. It knows +about quotes, escape characters and special command separators such +as ;, | and &&. If specified WORDBREAKS contains extra word breaks, +usually taken from COMP_WORDBREAKS, to apply while tokenizing. + +This method returns a list of tokens found between START and END, +ordered by position. Tokens contain a string and a range. + +The string in a token is an unescaped version of the token. For +example, if the token is 'hello world', the string contains +\"hello world\", without the quotes. It can be accessed using +`bash-completion-tokenize-get-str'. It can be modified using +`bash-completion-tokenize-append-str'. + +The range is a cons containing the start and end position of the +token (start . end). Start is the position of the first character +that belongs to the token. End is the position of the first +character that doesn't belong to the token. For example in the +string \" hello world \", the first token range is (2 . 7) and +the second token range (9 . 14). It can be accessed using +`bash-completion-tokenize-get-range'. The end position can be +set using `bash-completion-tokenize-set-end'. + +Tokens should always be accessed using the functions specified above, +never directly as they're likely to change as this code evolves. +The current format of a token is '(string . (start . end))." + (let ((tokens nil) + (bash-completion-wordbreaks + (mapconcat 'char-to-string + (delq nil (mapcar + (lambda (c) + (if (memq c '(?\; ?& ?| ?' ?\")) nil c)) + (or wordbreaks ""))) + ""))) + (save-excursion + (goto-char start) + (while (progn (skip-chars-forward " \t\r" end) + (< (point) end)) + (setq tokens + (bash-completion-tokenize-new-element end tokens))) + (nreverse tokens)))) + +(defun bash-completion-tokenize-new-element (limit tokens) + "Tokenize an element from point, up until LIMIT and complete TOKENS. + +This function is meant to be called exclusively from +`bash-completion-tokenize' and `bash-completion-tokenize-0'. + +This function expects the point to be at the start of a new +element to be added to the list of tokens. It parses the line +until the limit of that element or until LIMIT. + +It leaves the point at the position where parsing should +continue. + +Return TOKENS with new tokens prepended." + (skip-chars-forward " \t\r" limit) + (if (eq ?\n (char-after)) + (progn + (goto-char (1+ (point))) + (cons `((str . "\n") (range ,(point) . ,(1+ (point)))) tokens)) + (bash-completion-tokenize-0 + limit tokens + (list + (cons 'str "") + (cons 'range (cons (point) nil)))))) + +(defun bash-completion-tokenize-0 (end tokens token) + "Tokenize the rest of the token until END and add it into TOKENS. + +This function is meant to be called exclusively from +`bash-completion-tokenize-new-element'. + +This function expect the point to be at the start of a new token +section, either at the start of the token or just after a quote +has been closed in the token. It detects new opening quotes and +calls `bash-completion-tokenize-1'. + +END specifies the point at which tokenization should stop. + +TOKENS is the list of tokens built so farin reverse order. + +TOKEN is the token currently being built. + +Return TOKENS with new tokens prepended to it." + (let ((char-start (char-after)) + (quote nil) ) + (when (and char-start (or (= char-start ?') (= char-start ?\"))) + (forward-char) + (setq quote char-start)) + (bash-completion-tokenize-1 end quote tokens token))) + +(defun bash-completion-tokenize-1 (end quote tokens token) + "Tokenize the rest of the token. + +This function is meant to be called exclusively from +`bash-completion-tokenize-0'. + +This function tokenizes the rest of the token and either calls +itself and `bash-completion-tokenize-0' recursively or appends +the token to the list of token and calls +`bash-completion-tokenize-new-element' to look for the next +token. + +END specifies the point at which tokenization should stop, even +if the token is not complete. + +QUOTE specifies the current quote. It should be nil, ?' or ?\" + +TOKENS is the list of tokens built so far in reverse order. + +TOKEN is the token currently being built. + +Sets the point at the position of the next token. Returns TOKENS +with new tokens prepended to it." + ;; parse the token elements at the current position and + ;; append them + (let ((local-start (point))) + (when (= (skip-chars-forward + (concat "[;&|" bash-completion-wordbreaks "]") + end) + 0) + (skip-chars-forward + (bash-completion-nonsep quote bash-completion-wordbreaks) end)) + (bash-completion-tokenize-append-str + token + (buffer-substring-no-properties local-start (point)))) + (cond + ;; an escaped char, skip, whatever it is + ((and (char-before) (= ?\\ (char-before))) + (forward-char) + (let ((str (bash-completion-tokenize-get-str token))) + (aset str (1- (length str)) (char-before))) + (bash-completion-tokenize-1 end quote tokens token)) + ;; opening quote + ((and (not quote) (char-after) (or (= ?' (char-after)) (= ?\" (char-after)))) + (bash-completion-tokenize-0 end tokens token)) + ;; closing quote + ((and quote (char-after) (= quote (char-after))) + (forward-char) + (bash-completion-tokenize-0 end tokens token)) + ;; inside a quote + ((and quote (char-after) (not (= quote (char-after)))) + (forward-char) + (bash-completion-tokenize-append-str token (char-to-string (char-before))) + (bash-completion-tokenize-1 end quote tokens token)) + ;; word end + (t + (bash-completion-tokenize-set-end token) + (when quote + (push (cons 'quote quote) token)) + (push token tokens)))) + +(defun bash-completion-nonsep (quote wordbreaks) + "Return the set of non-breaking characters when QUOTE is the current quote. + +QUOTE should be nil, ?' or ?\"." + (concat + "^ \t\n\r" + (if (null quote) (concat ";&|'\"" wordbreaks) + (char-to-string quote)))) + +;;; ---------- Functions: getting candidates from bash + +(defun bash-completion-comm (comp process) + "Call compgen on COMP for PROCESS, return the result. + +COMP should be a struct returned by `bash-completion--parse' + +This function starts a separate bash process if necessary, sets +up the completion environment (COMP_LINE, COMP_POINT, COMP_WORDS, +COMP_CWORD) and calls compgen. + +The result is a list of candidates, which might be empty." + (let* ((buffer (bash-completion--get-buffer process)) + (cmd-timeout (if (eq 'custom (bash-completion--type comp)) + bash-completion-command-timeout + bash-completion-process-timeout)) + (completion-status)) + (setq completion-status (bash-completion-send + (bash-completion-generate-line comp) + process cmd-timeout comp)) + (when (eq 124 completion-status) + ;; Special 'retry-completion' exit status, typically returned by + ;; functions bound by complete -D. Presumably, the function has + ;; just setup completion for the current command and is asking + ;; us to retry once with the new configuration. + (bash-completion-send "complete -p" process cmd-timeout comp) + (process-put process 'complete-p (bash-completion-build-alist buffer)) + (bash-completion--customize comp process 'nodefault) + (setq completion-status (bash-completion-send + (bash-completion-generate-line comp) + process cmd-timeout comp))) + (when (eq 0 completion-status) + (bash-completion-extract-candidates comp buffer)))) + +(defun bash-completion-extract-candidates (comp buffer) + "Extract the completion candidates for COMP form BUFFER. + +This command takes the content of the completion process buffer, +splits it by newlines, post-process the candidates and returns +them as a list of strings. + +It should be invoked with the comint buffer as the current buffer +for directory name detection to work. + +Post-processing includes escaping special characters, adding a / +to directory names, replacing STUB with UNPARSED-STUB in the +result. See `bash-completion-fix' for more details." + (let ((output) (candidates) (pwd)) + (with-current-buffer buffer + (setq pwd (bash-completion--parse-side-channel-data "pwd")) + (let ((compopt (bash-completion--parse-side-channel-data "compopt"))) + (cond + ((string= "-o nospace" compopt) + (setf (bash-completion--compopt comp) '((nospace . t)))) + ((string= "+o nospace" compopt) + (setf (bash-completion--compopt comp) '((nospace . nil)))))) + (setq output (buffer-string))) + (setq candidates (delete-dups (split-string output "\n" t))) + (let ((default-directory (if pwd + (concat (file-remote-p default-directory) pwd) + default-directory))) + (if (eq 1 (length candidates)) + (list (bash-completion-fix (car candidates) comp t)) + ;; multiple candidates + (let ((result (list))) + (dolist (completion candidates) + (push (bash-completion-fix completion comp nil) result)) + (delete-dups (nreverse result))))))) + +(defun bash-completion-fix (str comp single) + "Fix completion candidate in STR for COMP + +STR is the completion candidate to modify, COMP the current +completion operation. + +If STR is the single candidate, SINGLE is t. + +Return a modified version of STR. + +Modification include: + - escaping of special characters in STR + - prepending the stub if STR does not contain all of it, when + completion was done after a wordbreak + - adding / to recognized directory names + +It should be invoked with the comint buffer as the current buffer +for directory name detection to work." + (let ((parsed-prefix (bash-completion--stub comp)) + (unparsed-prefix (bash-completion--unparsed-stub comp)) + (open-quote (bash-completion--open-quote comp)) + (nospace (bash-completion--nospace comp)) + (wordbreaks (bash-completion--wordbreaks comp)) + (suffix "") + (rest) ; the part between the prefix and the suffix + (rebuilt)) + + ;; build rest by removing parsed-prefix from str + (cond + ((bash-completion-starts-with str parsed-prefix) + (setq rest (substring str (length parsed-prefix)))) + + ;; completion sometimes only applies to the last word, as + ;; defined by COMP_WORDBREAKS. This detects and works around + ;; this feature. + ((bash-completion-starts-with + (setq rebuilt (concat (bash-completion-before-last-wordbreak parsed-prefix wordbreaks) str)) + parsed-prefix) + (setq rest (substring rebuilt (length parsed-prefix)))) + + ;; there is no meaningful link between the prefix and the string. + ;; Bypass the whole prefix/suffix logic and replace the string + ;; being completed with the string provided by the completion + ;; logic. + ((string-match "^~.*?\\($\\|/\\)" str) + (setq parsed-prefix (substring str 0 (match-end 0)) + unparsed-prefix + (concat (substring str 0 (match-end 0)) + (if open-quote (char-to-string open-quote) "")) + rest (substring str (match-end 0)))) + + (t + (setq parsed-prefix "" + unparsed-prefix (if open-quote (char-to-string open-quote) "") + rest str))) + + ;; build suffix + (let ((last-char (bash-completion-last-char rest)) + (close-quote-str (if open-quote (char-to-string open-quote) "")) + (final-space-str (if nospace "" " "))) + (cond + ((eq ?\ last-char) + (setq rest (substring rest 0 -1)) + (setq suffix (concat close-quote-str final-space-str))) + ((or (bash-completion--find-last last-char wordbreaks) + (eq ?/ last-char)) + (setq suffix "")) + ((file-accessible-directory-p + (bash-completion--expand-file-name (bash-completion-unescape + open-quote (concat parsed-prefix rest)))) + (setq suffix "/")) + (single + (setq suffix (concat close-quote-str final-space-str))) + (t (setq suffix close-quote-str)))) + + ;; put everything back together + (concat unparsed-prefix + (bash-completion-escape-candidate rest open-quote) + suffix))) + +(defun bash-completion-escape-candidate (completion-candidate open-quote) + "Escapes COMPLETION-CANDIDATE. + +This function escapes all special characters in the result of +bash completion. It does nothing if COMPLETION-CANDIDATE looks +like a quoted string. + +It uses escape characters appropriate for the quote defined in +OPEN-QUOTE, either nil, ' or \". + +Return a possibly escaped version of COMPLETION-CANDIDATE." + (cond + ((zerop (length completion-candidate)) "") + ((null open-quote) + (replace-regexp-in-string + "\n" "'\n'" + (replace-regexp-in-string + bash-completion-special-chars "\\\\\\&" completion-candidate))) + ((eq ?' open-quote) + (replace-regexp-in-string "'" "'\\''" completion-candidate nil t)) + ((eq ?\" open-quote) + ;; quote '$', '`' or '"' + (replace-regexp-in-string + "[$`\"]" "\\\\\\&" + ;; quote backslash if it's followed by '$', '`' or '"' + (replace-regexp-in-string "\\\\\\([$`\"]\\)" "\\\\\\\\\\1" completion-candidate))) + (t + completion-candidate))) + +(defun bash-completion-unescape (open-quote string) + "Unescapes a possibly QUOTE'ed STRING." + (if (eq ?' open-quote) + (replace-regexp-in-string "'\\\\''" "'" string) + (replace-regexp-in-string "\\(\\\\\\)\\(.\\)" "\\2" string))) + +(defun bash-completion-before-last-wordbreak (str wordbreaks) + "Return the part of STR that comes after the last WORDBREAKS character. +The return value does not include the worbreak character itself. + +If no wordbreak was found, it returns STR." + (nth 0 (bash-completion-last-wordbreak-split str wordbreaks))) + +(defun bash-completion-last-wordbreak-split (str wordbreaks) + "Split STR at the last WORDBREAKS character. + +The part before the last wordbreak character includes the +wordbreak character itself. It is \"\" if no wordbreak character +was found. + +The part after the last wordbreak character does not include the +wordbreak character. It is STR if no wordbreak character was +found. + +Return a CONS containing (before . after)." + (catch 'bash-completion-return + (let ((end (- (length str) 1))) + (while (>= end 0) + (when (bash-completion--find-last (aref str end) wordbreaks) + (throw 'bash-completion-return + (list (substring str 0 (1+ end)) + (substring str (1+ end)) + (aref str end)))) + (setq end (1- end)))) + (list "" str ?\0))) + +(defun bash-completion-last-char (str) + "Returns the last char of STR or nil." + (let ((str-len (length str))) + (and (>= str-len 1) + (aref str (1- str-len))))) + +(defun bash-completion-starts-with (str prefix) + "Return t if STR starts with PREFIX." + (let ((prefix-len (length prefix)) + (str-len (length str))) + (and + (>= str-len prefix-len) + (string= (substring str 0 prefix-len) prefix)))) + +;;; ---------- Functions: bash subprocess + +(defun bash-completion--get-or-create-separate-process () + "Return the bash completion process or start it. + +If a bash completion process is already running, return it. + +Otherwise, create a bash completion process and return the +result. This can take a since bash needs to start completely +before this function returns to be sure everything has been +initialized correctly. + +The process uses `bash-completion-prog' to figure out the path to +bash on the current system. + +To get an environment consistent with shells started with `shell', +the first file found in the following list are sourced if they exist: + ~/.emacs_bash.sh + ~/.emacs.d/init_bash.sh +Or, to be more exact, ~/.emacs_$(basename `bash-completion-prog').sh) +and ~/.emacs.d/init_$(basename `bash-completion-prog').sh) + +To allow scripts to tell the difference between shells launched +by bash-completion, the environment variable EMACS_BASH_COMPLETE +is set to t." + (let ((remote (file-remote-p default-directory))) + (if (bash-completion-is-running) + (cdr (assoc remote bash-completion-processes)) + ;; start process + (let (process (oldterm (getenv "TERM")) (cleanup t)) + (unwind-protect + (progn + (setenv "TERM" "dumb") + (setenv "EMACS_BASH_COMPLETE" "t") + (let* ((start-proc-fun (if remote #'start-file-process #'start-process)) + (buffer-name (generate-new-buffer-name " bash-completion")) + (args `("*bash-completion*" + ,buffer-name + ,(if remote bash-completion-remote-prog bash-completion-prog) + ,@bash-completion-args))) + (when remote + ;; See http://lists.gnu.org/archive/html/tramp-devel/2016-05/msg00004.html + (get-buffer-create buffer-name)) + (let ((non-essential (if remote nil non-essential))) + ;; Set `non-essential' to nil when spawning a remote + ;; shell to ensure that Tramp will try to open a + ;; connection to the remote host. Otherwise the + ;; process will be launched on the localhost. This + ;; is need because some completion framework (e.g + ;; company) set `non-essential' to a non-nil value + ;; when the completion has not been requested by the + ;; user + (setq process (apply start-proc-fun args)))) + (set-process-query-on-exit-flag process nil) + (if remote + ;; Set EMACS_BASH_COMPLETE now for remote + ;; completion, since setenv doesn't work. This will + ;; unfortunately not be available in .bashrc or + ;; .bash_profile. TODO: Find a way of getting it to + ;; work from the very beginning. + (process-send-string process "EMACS_BASH_COMPLETE=t\n")) + (dolist (start-file bash-completion-start-files) + (when (file-exists-p (bash-completion--expand-file-name start-file)) + (process-send-string process (concat ". " start-file "\n")))) + (process-send-string + process + (concat + ;; attempt to turn off unexpected status messages from + ;; bash if the current version of bash does not + ;; support these options, the commands will fail + ;; silently and be ignored. + "shopt -u checkjobs;" + "shopt -u mailwarn;" + "export MAILCHECK=-1;" + "export -n MAIL;" + "export -n MAILPATH;" + "unset HISTFILE;" + ;; User's profiles can turn line editing back on, + ;; so make sure it's off + "set +o emacs;" + "set +o vi\n")) + + (bash-completion-send + (concat "PROMPT_COMMAND='' PS1=" bash-completion--ps1) + process bash-completion-initial-timeout) + (bash-completion--setup-bash-common process) + (push (cons remote process) bash-completion-processes) + (setq cleanup nil) + process) + ;; finally + (progn + (setenv "EMACS_BASH_COMPLETE" nil) + (setenv "TERM" oldterm) + (when cleanup + (condition-case nil + (bash-completion-kill process) + (error nil))))))))) + +(defun bash-completion--current-shell () + "Figure out what the shell associated with the current buffer is." + (let ((prog (or + (if (derived-mode-p 'shell-mode) + (or explicit-shell-file-name + (getenv "ESHELL") + shell-file-name)) + (let ((process (get-buffer-process (current-buffer)))) + (when process + (car (process-command process))))))) + (when prog + (file-name-nondirectory prog)))) + +(defun bash-completion--get-same-process () + "Return the BASH process associated with the current buffer. + +Return nil if the current buffer is not a comint buffer or is not +associated with a command that looks like a bash shell. +Completion will fallback to creating a separate process +completion in these cases." + (when (derived-mode-p 'comint-mode) + (let* ((process (get-buffer-process (current-buffer))) + (shell (if process (bash-completion--current-shell)))) + (when (and shell (bash-completion-starts-with shell "bash")) + (unless (process-get process 'setup-done) + ;; The following disables the emacs and vi options. This + ;; cannot be done by bash-completion-send as these options + ;; interfere with bash-completion-send detecting the end + ;; of a command. It disables prompt to avoid interference + ;; from commands run by prompts. + (comint-send-string + process + (concat + "set +o emacs;" + "set +o vi;" + "if [[ -z \"$__emacs_complete_ps1\" ]]; then" + " __emacs_complete_ps1=\"$PS1\";" + " __emacs_complete_pc=\"$PROMPT_COMMAND\";" + "fi;" + "PS1='' PROMPT_COMMAND='';" + "history &>/dev/null -d $((HISTCMD - 1)) || true\n")) + + ;; The following is a bootstrap command for + ;; bash-completion-send itself. + (bash-completion-send + (concat + "function __emacs_complete_pre_command {" + " if [[ -z \"$__emacs_complete_ps1\" ]]; then" + " __emacs_complete_ps1=\"$PS1\";" + " __emacs_complete_pc=\"$PROMPT_COMMAND\";" + " fi;" + " PROMPT_COMMAND=__emacs_complete_prompt;" + " history &>/dev/null -d $((HISTCMD - 1)) || true;" + "} &&" + "function __emacs_complete_prompt {" + " PS1=" bash-completion--ps1 ";" + " PROMPT_COMMAND=__emacs_complete_recover_prompt;" + "} &&" + "function __emacs_complete_recover_prompt {" + " local r=$?;" + " PS1=\"${__emacs_complete_ps1}\";" + " PROMPT_COMMAND=\"${__emacs_complete_pc}\";" + " unset __emacs_complete_ps1 __emacs_complete_pc;" + " if [[ -n \"$PROMPT_COMMAND\" ]]; then" + " (exit $r); eval \"$PROMPT_COMMAND\";" + " fi;" + "} &&" + "__emacs_complete_pre_command") + process) + (bash-completion--setup-bash-common process)) + process)))) + +(defun bash-completion--get-process () + "Setup and return a bash completion process. + +If `bash-completion-use-separate-processes' is non-nil, +`bash-completion--get-or-create-separate-process' is called to +get the process, otherwise `bash-completion--get-same-process' is +used. " + (if bash-completion-use-separate-processes + (bash-completion--get-or-create-separate-process) + (bash-completion--get-same-process))) + +(defun bash-completion-cd-command-prefix () + "Build a command-line that CD to default-directory. + +Return a bash command-line for going to default-directory or \"\"." + (let ((dir (or (file-remote-p (or default-directory "") 'localname) + default-directory))) + (if dir + (concat "cd >/dev/null 2>&1 " + (bash-completion-quote (bash-completion--expand-file-name dir t)) + " && ") + ""))) + +(defun bash-completion-build-alist (buffer) + "Parse the content of BUFFER into an alist. + +BUFFER should contains the output of: + complete -p + +The returned alist is a slightly parsed version of the output of +\"complete -p\"." + (let ((alist (list))) + (with-current-buffer buffer + (save-excursion + (goto-char (point-min)) + (when (search-forward-regexp "^complete" nil 'noerror) + (let ((tokens (bash-completion-strings-from-tokens + (bash-completion-tokenize + (match-beginning 0) (point-max))))) + (while tokens + (let ((command tokens) + (command-end (member "\n" tokens))) + (setq tokens (cdr command-end)) + (when command-end + (setcdr command-end nil)) + (when (string= "complete" (car command)) + (setq command (nreverse (cdr command))) + (when (equal "\n" (car command)) + (setq command (cdr command))) + (if (member "-D" command) + ;; default completion + (push (cons nil (nreverse (delete "-D" command))) alist) + ;; normal completion + (let ((command-name (car command)) + (options (nreverse (cdr command)))) + (when (and command-name options) + (push (cons command-name options) alist))))))))))) + (nreverse alist))) + +(defun bash-completion--customize (comp process &optional nodefault) + (unless (eq 'command (bash-completion--type comp)) + (let ((compgen-args-alist + (process-get process 'complete-p)) + (command-name (bash-completion--command comp))) + ;; TODO: first lookup the full command path, then only the + ;; command name. + (setf (bash-completion--compgen-args comp) + (or (cdr (assoc command-name compgen-args-alist)) + (and (not nodefault) (cdr (assoc nil compgen-args-alist)))))))) + +(defun bash-completion-generate-line (comp) + "Generate a command-line that calls compgen for COMP. + +COMP is a struct returned by `bash-completion--parse'. It is +normally configured using `bash-completion--customize' before +calling this command. + +If the compgen argument set specifies a custom function or command, the +arguments will be passed to this function or command as: + COMP_LINE, taken from (bash-completion--line COMP) + COMP_POINT, taken from (bash-completion--point COMP) + COMP_WORDS, taken from (bash-completion--words COMP) (a bash array) + COMP_CWORD, taken for (bash-completion--cword COMP) + +Return a cons containing the completion type (command default or +custom) and a bash command-line that calls compgen to get the +completion candidates." + (let ((quoted-stub (bash-completion-quote (bash-completion--stub comp))) + (completion-type (bash-completion--type comp)) + (compgen-args (bash-completion--compgen-args comp))) + (concat + (if bash-completion-use-separate-processes + (bash-completion-cd-command-prefix) + (bash-completion--side-channel-data "pwd" "${PWD}")) + (cond + ((eq 'command completion-type) + (concat "compgen -b -c -a -A function -- " quoted-stub)) + + ((eq 'default completion-type) + (concat "compgen -o default -- " quoted-stub)) + + ((and (eq 'custom completion-type) (or (member "-F" compgen-args) + (member "-C" compgen-args))) + ;; custom completion with a function of command + (let* ((args (copy-tree compgen-args)) + (function (or (member "-F" args) (member "-C" args))) + (function-name (car (cdr function)))) + (setcar function "-F") + (setcar (cdr function) "__emacs_complete_wrapper") + (format "__EMACS_COMPLETE_WRAPPER=%s compgen %s -- %s" + (bash-completion-quote + (format "COMP_LINE=%s; COMP_POINT=$(( 1 + ${#COMP_LINE} )); COMP_CWORD=%s; COMP_WORDS=( %s ); %s %s %s %s" + (bash-completion-quote (bash-completion--line comp)) + (bash-completion--cword comp) + (bash-completion-join (bash-completion--words comp)) + (bash-completion-quote function-name) + (bash-completion-quote (bash-completion--command comp)) + (bash-completion-quote (bash-completion--stub comp)) + (bash-completion-quote (or (nth (1- (bash-completion--cword comp)) + (bash-completion--words comp)) + "")))) + (bash-completion-join args) + quoted-stub))) + ((eq 'custom completion-type) + ;; simple custom completion + (format "compgen %s -- %s" + (bash-completion-join compgen-args) + quoted-stub)) + (t (error "Unsupported completion type: %s" completion-type))) + " 2>/dev/null"))) + +;;;###autoload +(defun bash-completion-refresh () + "Force a refresh the completion table. + +This can be called after changing the completion table on BASH, +or after starting a new BASH job. + +This is only useful when `bash-completion-use-separate-processes' +is t." + (interactive) + (let* ((process (get-buffer-process (current-buffer)))) + (unless process + (error "No process is available in this buffer.")) + (process-put process 'setup-done nil) + (bash-completion--get-process))) + +;;;###autoload +(defun bash-completion-reset () + "Force the next completion command to start with a fresh BASH process. + +This function kills any existing BASH completion process. This +way, the next time BASH completion is requested, a new process +will be created with the latest configuration. The BASH +completion process that will be killed depends on the +default-directory of the buffer where the command is executed. + +Call this method if you have updated your .bashrc or any bash init scripts +and would like bash completion in Emacs to take these changes into account." + (interactive) + (let* ((remote (and default-directory (file-remote-p default-directory))) + (entry (assoc remote bash-completion-processes)) + (proc (cdr entry))) + (when proc + (bash-completion-kill proc) + (setq bash-completion-processes (delq entry bash-completion-processes))))) + +(defun bash-completion-reset-all () + (interactive) + (mapcar (lambda (entry) + (let ((default-directory (car entry))) + (bash-completion-reset))) + bash-completion-processes)) + +(defun bash-completion-kill (process) + "Kill PROCESS and its buffer." + (when process + (when (eq 'run (process-status process)) + (kill-process process)) + (let ((buffer (bash-completion--get-buffer process))) + (when (buffer-live-p buffer) + (kill-buffer buffer))))) + +(defun bash-completion-buffer () + "Return the buffer of the BASH process, create the BASH process if necessary." + (bash-completion--get-buffer (bash-completion--get-process))) + +(defun bash-completion-is-running () + "Check whether the bash completion process is running." + (let* ((entry (assoc (file-remote-p default-directory) + bash-completion-processes)) + (proc (cdr entry)) + (running (and proc (eq 'run (process-status proc))))) + (unless (and entry running) + (setq bash-completion-processes (delq entry bash-completion-processes))) + running)) + +(defun bash-completion--output-filter (output) + (with-current-buffer (bash-completion--get-buffer nil) + (let ((begin (point-max))) + (goto-char begin) + (insert output) + (save-excursion + (goto-char (point-min)) + (while (search-forward "\r" nil 'noerror) + (delete-char -1))) + (ansi-color-filter-region begin (point)) + ""))) + +(defun bash-completion--wait-for-regexp (process prompt-regexp timeout &optional limit) + (let ((no-timeout t)) + (while (and no-timeout + (not (re-search-backward prompt-regexp limit t))) + (setq no-timeout (accept-process-output process timeout nil t))) + no-timeout)) + +(defun bash-completion-send (commandline &optional process timeout debug-context) + "Send a command to the bash completion process. + +COMMANDLINE should be a bash command, without the final newline. + +PROCESS should be the bash process, if nil this function calls +`bash-completion--get-process' which might start a new process +depending on the value of +`bash-completion-use-separate-processes'. + +TIMEOUT is the timeout value for this operation, if nil the value of +`bash-completion-process-timeout' is used. + +DEBUG-CONTEXT, if specified, is appended to the debug info under +the key 'debug-context. + +Once this command has run without errors, you will find the +result of the command in the bash completion process buffer or in +`bash-completion-output-buffer' if +`bash-completion-use-separate-processes' is nil. + +Return the status code of the command, as a number." + (let* ((process (or process (bash-completion--get-process))) + (timeout (or timeout bash-completion-process-timeout)) + (comint-preoutput-filter-functions + (if bash-completion-use-separate-processes + comint-preoutput-filter-functions + '(bash-completion--output-filter))) + (send-string (if bash-completion-use-separate-processes + #'process-send-string + #'comint-send-string)) + (pre-command (unless bash-completion-use-separate-processes + "__emacs_complete_pre_command; ")) + (complete-command (concat pre-command commandline "\n"))) + (setq bash-completion--debug-info + (list (cons 'commandline complete-command) + (cons 'process process) + (cons 'use-separate-processes bash-completion-use-separate-processes) + (cons 'context debug-context))) + (with-current-buffer (bash-completion--get-buffer process) + (erase-buffer) + (funcall send-string process complete-command) + (unless (bash-completion--wait-for-regexp process "\t-?[[:digit:]]+\v" timeout) + (push (cons 'error "timeout") bash-completion--debug-info) + (push (cons 'buffer-string (buffer-substring-no-properties (point-min) (point-max))) + bash-completion--debug-info) + (error "Bash completion failed. M-x bash-completion-debug for details.")) + (when pre-command + ;; Detect the command having been echoed and remove it + (save-excursion + (goto-char (point-min)) + (when (looking-at pre-command) + (delete-region (match-beginning 0) (line-beginning-position 2))))) + (let ((status (string-to-number + (buffer-substring-no-properties + (1+ (point)) + (1- (line-end-position))))) + (wrapped-status (bash-completion--parse-side-channel-data "wrapped-status"))) + (push (cons 'status status) bash-completion--debug-info) + (push (cons 'wrapped-status wrapped-status) bash-completion--debug-info) + (delete-region (point) (point-max)) + (if (string= "124" wrapped-status) + 124 + status))))) + +(defun bash-completion-debug () + (interactive) + (with-help-window "*bash-completion-debug*" + (unless bash-completion--debug-info + (error "No debug information available for bash-completion. Please try it out first.")) + (princ "This buffer contains information about the last completion command\n") + (princ "and the BASH process it was sent to. This can help you figure out\n") + (princ "what's happening.\n\n") + (princ "If it doesn't, go to\n") + (princ "https://github.com/szermatt/emacs-bash-completion/issues/new\n") + (princ "to create a new issue that describes:\n") + (princ "- what you were trying to do\n") + (princ "- what you expected to happen\n") + (princ "- what actually happened\n\n") + (princ "Then add a copy of the information below:\n\n") + (bash-completion--debug-print-info 'commandline 'eof) + (bash-completion--debug-print-info 'error) + (bash-completion--debug-print-info 'buffer-string 'eof) + (bash-completion--debug-print-info 'status) + (bash-completion--debug-print-info 'wrapped-status) + (bash-completion--debug-print-info 'process) + (bash-completion--debug-print-info 'use-separate-processes) + + (let* ((debug-info bash-completion--debug-info) + (process (cdr (assq 'process debug-info))) + (bash-completion-use-separate-processes + (cdr (assq 'use-separate-processes debug-info)))) + (if (process-live-p process) + (bash-completion--debug-print + 'output-buffer + (with-current-buffer (bash-completion--get-buffer process) + (buffer-substring-no-properties (point-min) (point-max))) + 'eof) + (princ "\nERROR: Process is dead. ") + (princ "Information collection is incomplete.\n") + (princ "Please retry\n\n"))) + + (bash-completion--debug-print-info 'use-separate-processes) + (bash-completion--debug-print-procinfo 'bash-major-version) + (bash-completion--debug-print 'emacs-version emacs-version) + (bash-completion--debug-print-procinfo 'completion-ignore-case) + (bash-completion--debug-print-info 'context) + (bash-completion--debug-print-procinfo 'complete-p))) + +(defun bash-completion--debug-print-info (symbol &optional eof) + "Print variable SYMBOL from `bash-completion-debug-info'. + +If EOF is non-nil, VALUE might contain newlines and other special +characters. These are output as-is." + (bash-completion--debug-print + symbol (cdr (assq symbol bash-completion--debug-info)) eof)) + +(defun bash-completion--debug-print-procinfo (symbol &optional eof) + "Print variable SYMBOL from `bash-completion-debug-info''s process. + +If EOF is non-nil, VALUE might contain newlines and other special +characters. These are output as-is." + (let ((process (cdr (assq 'process bash-completion--debug-info)))) + (when (process-live-p process) + (bash-completion--debug-print + symbol (process-get process symbol) eof)))) + +(defun bash-completion--debug-print (name value &optional eof) + "Print debugging information NAME and VALUE. + +If EOF is non-nil, VALUE might contain newlines and other special +characters. These are output as-is." + (when value + (princ name) + (princ ": ") + (if eof + (progn + (princ "< + +;; Author: Lucas Bonnet +;; Keywords: lisp,convenience +;; Version: 0.5 +;; URL : https://github.com/lukhas/buffer-move + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 2 +;; of the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, write to the Free Software +;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +;;; Commentary: + +;; This file is for lazy people wanting to swap buffers without +;; typing C-x b on each window. This is useful when you have : + +;; +--------------+-------------+ +;; | | | +;; | #emacs | #gnus | +;; | | | +;; +--------------+-------------+ +;; | | +;; | .emacs | +;; | | +;; +----------------------------+ + +;; and you want to have : + +;; +--------------+-------------+ +;; | | | +;; | #gnus | .emacs | +;; | | | +;; +--------------+-------------+ +;; | | +;; | #emacs | +;; | | +;; +----------------------------+ + +;; With buffer-move, just go in #gnus, do buf-move-left, go to #emacs +;; (which now should be on top right) and do buf-move-down. + +;; To use it, simply put a (require 'buffer-move) in your ~/.emacs and +;; define some keybindings. For example, i use : + +;; (global-set-key (kbd "") 'buf-move-up) +;; (global-set-key (kbd "") 'buf-move-down) +;; (global-set-key (kbd "") 'buf-move-left) +;; (global-set-key (kbd "") 'buf-move-right) + + +;;; Code: + + +(require 'windmove) + +;;;###autoload +(defun buf-move-up () + "Swap the current buffer and the buffer above the split. +If there is no split, ie now window above the current one, an +error is signaled." +;; "Switches between the current buffer, and the buffer above the +;; split, if possible." + (interactive) + (let* ((other-win (windmove-find-other-window 'up)) + (buf-this-buf (window-buffer (selected-window)))) + (if (null other-win) + (error "No window above this one") + ;; swap top with this one + (set-window-buffer (selected-window) (window-buffer other-win)) + ;; move this one to top + (set-window-buffer other-win buf-this-buf) + (select-window other-win)))) + +;;;###autoload +(defun buf-move-down () +"Swap the current buffer and the buffer under the split. +If there is no split, ie now window under the current one, an +error is signaled." + (interactive) + (let* ((other-win (windmove-find-other-window 'down)) + (buf-this-buf (window-buffer (selected-window)))) + (if (or (null other-win) + (string-match "^ \\*Minibuf" (buffer-name (window-buffer other-win)))) + (error "No window under this one") + ;; swap top with this one + (set-window-buffer (selected-window) (window-buffer other-win)) + ;; move this one to top + (set-window-buffer other-win buf-this-buf) + (select-window other-win)))) + +;;;###autoload +(defun buf-move-left () +"Swap the current buffer and the buffer on the left of the split. +If there is no split, ie now window on the left of the current +one, an error is signaled." + (interactive) + (let* ((other-win (windmove-find-other-window 'left)) + (buf-this-buf (window-buffer (selected-window)))) + (if (null other-win) + (error "No left split") + ;; swap top with this one + (set-window-buffer (selected-window) (window-buffer other-win)) + ;; move this one to top + (set-window-buffer other-win buf-this-buf) + (select-window other-win)))) + +;;;###autoload +(defun buf-move-right () +"Swap the current buffer and the buffer on the right of the split. +If there is no split, ie now window on the right of the current +one, an error is signaled." + (interactive) + (let* ((other-win (windmove-find-other-window 'right)) + (buf-this-buf (window-buffer (selected-window)))) + (if (null other-win) + (error "No right split") + ;; swap top with this one + (set-window-buffer (selected-window) (window-buffer other-win)) + ;; move this one to top + (set-window-buffer other-win buf-this-buf) + (select-window other-win)))) + + +(provide 'buffer-move) +;;; buffer-move.el ends here diff --git a/.emacs.d/plugins/dired-sidebar.el b/.emacs.d/plugins/dired-sidebar.el new file mode 100755 index 0000000..6308ca8 --- /dev/null +++ b/.emacs.d/plugins/dired-sidebar.el @@ -0,0 +1,1195 @@ +;;; dired-sidebar.el --- Tree browser leveraging dired -*- lexical-binding: t -*- + +;; Copyright (C) 2017 James Nguyen + +;; Author: James Nguyen +;; Maintainer: James Nguyen +;; URL: https://github.com/jojojames/dired-sidebar +;; Version: 0.0.1 +;; Package-Requires: ((emacs "25.1") (dired-subtree "0.0.1")) +;; Keywords: dired, files, tools +;; HomePage: https://github.com/jojojames/dired-sidebar + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; This package provides a tree browser similar to `neotree' or `treemacs' +;; but leverages `dired' to do the job of display. + +;; (use-package dired-sidebar +;; :bind (("C-x C-n" . dired-sidebar-toggle-sidebar)) +;; :ensure nil +;; :commands (dired-sidebar-toggle-sidebar)) + +;;; Code: + +(require 'dired) +(require 'dired-subtree) +(require 'face-remap) +(eval-when-compile (require 'subr-x)) ; `if-let*' and `when-let*' + +;; Compatibility + +(eval-and-compile + (with-no-warnings + (if (version< emacs-version "26") + (progn + (defalias 'dired-sidebar-if-let* #'if-let) + (defalias 'dired-sidebar-when-let* #'when-let) + (function-put #'dired-sidebar-if-let* 'lisp-indent-function 2) + (function-put #'dired-sidebar-when-let* 'lisp-indent-function 1)) + (defalias 'dired-sidebar-if-let* #'if-let*) + (defalias 'dired-sidebar-when-let* #'when-let*)))) + +;; Customizations + +(defgroup dired-sidebar nil + "A major mode leveraging `dired-mode' to display a filesystem in a tree +layout." + :group 'files) + +(defcustom dired-sidebar-use-custom-font nil + "Show `dired-sidebar' with custom font. + +This face can be customized using `dired-sidebar-face'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-face nil + "Face used by `dired-sidebar' for custom font. + +This only takes effect if `dired-sidebar-use-custom-font' is true." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-custom-modeline t + "Show `dired-sidebar' with custom modeline. + +This uses format specified by `dired-sidebar-mode-line-format'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-mode-line-format + '("%e" mode-line-front-space + mode-line-buffer-identification + " " mode-line-end-spaces) + "Mode line format for `dired-sidebar'." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-theme 'icons + "*The tree style to display. +`ascii' is the simplest style, it will use +/- to display the fold state, +it is suitable for terminal. +`icons' use `all-the-icons'. +`nerd' use the nerdtree indentation mode and arrow. +`none' use no theme. +`vscode' use `vscode' icons. + +This only takes effect if on a local connection. (e.g. Not Tramp)" + :group 'dired-sidebar + :type '(choice (const ascii) + (const icons) + (const nerd) + (const none) + (const vscode))) + +(defcustom dired-sidebar-width 35 + "Width of the `dired-sidebar' buffer." + :type 'integer + :group 'dired-sidebar) + +(defcustom dired-sidebar-refresh-on-projectile-switch t + "Refresh sidebar when `projectile' changes projects." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-should-follow-file nil + "Refresh sidebar to match current file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-skip-subtree-parent t + "Whether to skip subtree parent directory when jumping up." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-pop-to-sidebar-on-toggle-open t + "Whether to jump to sidebar upon toggling open. + +This is used in conjunction with `dired-sidebar-toggle-sidebar'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-follow-file-at-point-on-toggle-open t + "Whether to recursively cycle the subtree and put point on file. + +Similar to `dired-jump'. This moves point inside sidebar buffer +to where current-buffer-file is \(if it exists\) but does not necessarily +select the sidebar window." + :type 'boolean + :group 'dired-sidebar) + + + +(defcustom dired-sidebar-use-magit-integration t + "Whether to integrate with `magit-mode'. + +When true: + +When finding file to point at for +`dired-sidebar-follow-file-at-point-on-toggle-open', use file at point +in `magit' buffer. + +When finding root directory for sidebar, use directory specified by `magit'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-term-integration nil + "Whether to integrate with `term-mode'. + +When true: + +When finding root directory for sidebar, use PWD of `term-mode'. This is turned +off by default due to the experimental nature of getting the PWD from the +terminal. + +Look at `dired-sidebar-term-get-pwd' for implementation." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-use-wdired-integration t + "Whether to integrate with `wdired'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-cycle-subtree-on-click t + "Whether to cycle subtree on click." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-delay-auto-revert-updates t + "Whether to delay automatically reverting buffer. + +When true, only allow function `auto-revert-mode' to update every +`dird-sidebar-stale-buffer-time-idle-delay' seconds." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-stale-buffer-time-idle-delay 1.5 + "The time in idle seconds to wait before checking if buffer is stale." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-follow-file-idle-delay 2 + "The time in idle seconds to wait before checking if sidebar should +follow file." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-tui-update-delay 0.02 + "The time in idle seconds to wait before updating tui interface. + +This only takes effect if `all-the-icons-dired' is disabled." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-refresh-on-special-commands t + "Whether or not to trigger auto-revert after certain functions. + +Warning: This is implemented by advising specific dired functions." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-disable-dired-collapse t + "Whether or not to disable `dired-collapse' if it's enabled." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-special-refresh-commands + '(dired-do-delete + dired-do-rename + dired-do-copy + dired-do-flagged-delete + dired-create-directory + (delete-file . 5) + (save-buffer . 5) + magit-format-patch) + "A list of commands that will trigger a refresh of the sidebar. + +The command can be an alist with the CDR of the alist being the amount of time +to wait to refresh the sidebar after the CAR of the alist is called. + +Set this to nil or set `dired-sidebar-refresh-on-special-commands' to nil +to disable automatic refresh when a special command is triggered." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-toggle-hidden-commands + '(balance-windows) + "A list of commands that won't work when `dired-sidebar' is visible. + +When the command is triggered, `dired-sidebar' will hide temporarily until +command is completed. + +This functionality is implemented using advice. + +Set this to nil to disable this advice." + :type 'list + :group 'dired-sidebar) + +(defcustom dired-sidebar-alternate-select-window-function + #'dired-sidebar-default-alternate-select-window + "Function to call when using alternative window selection. + +Alternative window selection is used when `dired-sidebar-find-file' is called +with a prefix arg or when `dired-sidebar-find-file-alt' is called." + :type 'function + :group 'dired-sidebar) + +(defcustom dired-sidebar-recenter-cursor-on-follow-file t + "Whether or not to center cursor when pointing at file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-recenter-cursor-on-tui-update nil + "Whether or not to center cursor when updating tui interface." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-display-autorevert-messages nil + "Whether or not to display `autorevert' messages." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-open-file-in-most-recently-used-window t + "Whether or not to open files in most recently used window." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-subtree-line-prefix dired-subtree-line-prefix + "The line prefix to use when subtree is cycled." + :type 'string + :group 'dired-sidebar) + +(defcustom dired-sidebar-display-alist '((side . left) (slot . -1)) + "Alist used in `display-buffer-in-side-window'. + +e.g. (display-buffer-in-side-window buffer '((side . left) (slot . -1)))" + :type 'alist + :group 'dired-sidebar) + +(defcustom dired-sidebar-close-sidebar-on-file-open nil + "Whether or not to close sidebar when `dired-sidebar-find-file' is called. + +This behavior only triggers if `dired-sidebar-find-file' is triggered on +a file." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-icon-scale .18 + "The scale of icons \(currently only applies to vscode theme.\)." + :type 'number + :group 'dired-sidebar) + +(defcustom dired-sidebar-no-delete-other-windows nil + "Whether or not to add `no-delete-other-window' parameter to window. + +If this is true, when calling `delete-other-windows', `dired-sidebar' window +will continue showing. + +For more information, look up `delete-other-windows'." + :type 'boolean + :group 'dired-sidebar) + +(defcustom dired-sidebar-one-instance-p nil + "Only show one buffer instance for dired-sidebar for each frame." + :type 'boolean + :group 'dired-sidebar) + +;; Internal + +(defvar dired-sidebar-basedir (file-name-directory load-file-name) + "Store the directory dired-sidebar.el was loaded from.") + +(defvar dired-sidebar-icons-dir (format "%sicons/" dired-sidebar-basedir) + "Store the icons directory of `dired-sidebar'.") + +(defvar dired-sidebar-alist '() + "An alist that maps from frame to currently opened `dired-sidebar' buffer.") + +(defvar-local dired-sidebar-stale-buffer-timer nil + "Timer used for setting `dired-sidebar-check-for-stale-buffer-p'. + +This is buffer local.") + +(defvar-local dired-sidebar-follow-file-timer nil + "Timer used when `dired-sidebar-should-follow-file' is true.") + +(defvar-local dired-sidebar-check-for-stale-buffer-p nil + "Whether to check if buffer is stale. + +When this is true `dired-sidebar-buffer-stale-p' +will check if buffer is stale through `auto-revert-mode'.") + +;; Mode + +(defmacro dired-sidebar-with-no-dedication (&rest body) + "Run BODY after undedicating window." + (declare (debug (&rest form))) + `(progn + (let ((window (get-buffer-window (current-buffer)))) + (set-window-dedicated-p window nil) + ,@body + (set-window-dedicated-p window t)))) + +(defvar dired-sidebar-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "TAB") 'dired-sidebar-subtree-toggle) + (define-key map [tab] 'dired-sidebar-subtree-toggle) + (define-key map (kbd "C-m") 'dired-sidebar-find-file) + (define-key map (kbd "RET") 'dired-sidebar-find-file) + (define-key map (kbd "") 'dired-sidebar-find-file) + (define-key map "^" 'dired-sidebar-up-directory) + (define-key map "-" 'dired-sidebar-up-directory) + (define-key map (kbd "C-o") 'dired-sidebar-find-file-alt) + (define-key map [mouse-2] 'dired-sidebar-mouse-subtree-cycle-or-find-file) + map) + "Keymap used for symbol `dired-sidebar-mode'.") + +(define-derived-mode dired-sidebar-mode dired-mode + "Dired-sidebar" + "A major mode that puts `dired' in a sidebar." + :group 'dired-sidebar + + ;; Hack for https://github.com/jojojames/dired-sidebar/issues/18. + ;; Would be open to a better fix... + ;; `dired-remember-hidden' in Emacs 25 (terminal?) seems to throw + ;; an error upon calling `goto-char'. + (when (<= emacs-major-version 25) + (defun dired-sidebar-remember-hidden-hack (f &rest args) + "Return nil for `dired-remember-hidden'. + +Works around marker pointing to wrong buffer in Emacs 25." + (if (eq major-mode 'dired-sidebar-mode) + nil + (apply f args))) + (advice-remove 'dired-remember-hidden 'dired-sidebar-remember-hidden-hack) + (advice-add 'dired-remember-hidden :around 'dired-sidebar-remember-hidden-hack)) + + ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392 + (when dired-sidebar-use-wdired-integration + (advice-remove 'wdired-change-to-dired-mode + 'dired-sidebar-wdired-change-to-dired-mode-advice) + (advice-remove 'wdired-change-to-wdired-mode + 'dired-sidebar-wdired-change-to-wdired-mode-advice) + + (advice-add 'wdired-change-to-dired-mode + :around 'dired-sidebar-wdired-change-to-dired-mode-advice) + (advice-add 'wdired-change-to-wdired-mode + :around 'dired-sidebar-wdired-change-to-wdired-mode-advice)) + + (setq window-size-fixed 'width) + + ;; Match backgrounds. + (setq-local dired-subtree-use-backgrounds nil) + + ;; `dired-subtree''s line prefix is determined by `dired-sidebar'. + (setq-local dired-subtree-line-prefix dired-sidebar-subtree-line-prefix) + + ;; https://github.com/jojojames/dired-sidebar/issues/7 + ;; Symlinks are displayed incorrectly when these three things happen. + ;; 1. `dired-hide-details-mode' is on. + ;; 2. `dired-subtree' toggles a symlink folder via `dired-subtree-toggle'. + ;; 3. `dired-hide-details-hide-symlink-targets' is set to true. + ;; Since we use both 1 & 2, disable 3 to avoid the issue. + ;; This needs to be set to nil before `dired-hide-details-mode' is called. + (setq-local dired-hide-details-hide-symlink-targets nil) + + ;; Use `dired-sidebar-revert' instead that wraps `dired-revert'. + (setq-local revert-buffer-function 'dired-sidebar-revert) + + ;; We don't want extra details in the sidebar. + (dired-hide-details-mode) + + (when (and dired-sidebar-disable-dired-collapse + (fboundp 'dired-collapse-mode)) + (add-hook 'dired-mode-hook + (lambda () + (when (bound-and-true-p dired-collapse-mode) + (dired-collapse-mode -1))) + :append :local)) + + (when (and + (not dired-sidebar-display-autorevert-messages) + (boundp 'auto-revert-verbose)) + (setq-local auto-revert-verbose nil)) + + (when dired-sidebar-delay-auto-revert-updates + (setq-local buffer-stale-function #'dired-sidebar-buffer-stale-p) + (let ((current-buffer (current-buffer))) + (setq dired-sidebar-stale-buffer-timer + (run-with-idle-timer + dired-sidebar-stale-buffer-time-idle-delay + t (lambda () + ;; Only do a check if `dired-sidebar' buffer is in the foreground. + (when (get-buffer-window current-buffer) + (with-current-buffer current-buffer + (setq dired-sidebar-check-for-stale-buffer-p t)))))) + + (add-hook 'kill-buffer-hook + (lambda () + (when (timerp dired-sidebar-stale-buffer-timer) + (cancel-timer dired-sidebar-stale-buffer-timer))) + nil t))) + + (when dired-sidebar-refresh-on-special-commands + (mapc + (lambda (x) + (if (consp x) + (let ((command (car x)) + (delay (cdr x))) + (advice-add + command + :after + (defalias (intern (format "dired-sidebar-refresh-after-%S" command)) + (function + (lambda (&rest _) + (let ((timer-symbol + (intern + (format + "dired-sidebar-refresh-%S-timer" command)))) + (when (and (boundp timer-symbol) + (timerp (symbol-value timer-symbol))) + (cancel-timer (symbol-value timer-symbol))) + (setf + (symbol-value timer-symbol) + (run-with-idle-timer + delay + nil + #'dired-sidebar-refresh-buffer)))))))) + (advice-add x :after #'dired-sidebar-refresh-buffer))) + dired-sidebar-special-refresh-commands)) + + (when dired-sidebar-toggle-hidden-commands + (mapc + (lambda (x) + (advice-add x :around #'dired-sidebar-advice-hide-temporarily)) + dired-sidebar-toggle-hidden-commands)) + + (when dired-sidebar-use-custom-font + (dired-sidebar-set-font)) + + (when dired-sidebar-use-custom-modeline + (dired-sidebar-set-mode-line)) + + (when dired-sidebar-refresh-on-projectile-switch + (add-hook 'projectile-after-switch-project-hook + #'dired-sidebar-follow-file)) + + (when dired-sidebar-should-follow-file + (setq dired-sidebar-follow-file-timer + (run-with-idle-timer + dired-sidebar-follow-file-idle-delay + t #'dired-sidebar-follow-file))) + + ;; This comment is taken from `dired-readin'. + ;; Begin --- Copied comment from dired.el. + ;; Must first make alist buffer local and set it to nil because + ;; dired-build-subdir-alist will call dired-clear-alist first + ;; End --- Copied comment from dired.el. + (setq-local dired-subdir-alist nil) + (dired-build-subdir-alist) + + (dired-unadvertise (dired-current-directory)) + (dired-sidebar-update-buffer-name) + (dired-sidebar-update-state (current-buffer)) + + ;; Move setting theme until the end after `dired-sidebar' has set up + ;; its directory structure. + ;; https://github.com/jojojames/dired-sidebar/issues/29 + (unless (file-remote-p default-directory) + (cond + ((dired-sidebar-using-tui-p) + (dired-sidebar-setup-tui)) + ((and (eq dired-sidebar-theme 'icons) + (display-graphic-p) + (or + (fboundp 'all-the-icons-dired-mode) + (autoloadp (symbol-function 'all-the-icons-dired-mode)))) + (with-no-warnings + (all-the-icons-dired-mode))) + (:default :no-theme)))) + +;; User Interface + +;;;###autoload +(defun dired-sidebar-toggle-sidebar (&optional dir) + "Toggle the project explorer window. +Optional argument DIR Use DIR as sidebar root if available. + +With universal argument, use current directory." + (interactive) + (if (dired-sidebar-showing-sidebar-p) + (dired-sidebar-hide-sidebar) + (let* ((old-buffer (dired-sidebar-buffer (selected-frame))) + (file-to-show (dired-sidebar-get-file-to-show)) + (dir-to-show (or dir + (when current-prefix-arg + (expand-file-name default-directory)) + (dired-sidebar-get-dir-to-show))) + (sidebar-buffer (dired-sidebar-get-or-create-buffer dir-to-show))) + (dired-sidebar-show-sidebar sidebar-buffer) + (when (and dired-sidebar-one-instance-p old-buffer (not (eq sidebar-buffer old-buffer))) + (kill-buffer old-buffer)) + (if (and dired-sidebar-follow-file-at-point-on-toggle-open + file-to-show) + (if dired-sidebar-pop-to-sidebar-on-toggle-open + (dired-sidebar-point-at-file file-to-show dir-to-show) + (with-selected-window (selected-window) + (dired-sidebar-point-at-file file-to-show dir-to-show))) + (when dired-sidebar-pop-to-sidebar-on-toggle-open + (pop-to-buffer (dired-sidebar-buffer))))))) + +(defun dired-sidebar-point-at-file (name root) + "Try to point at NAME from sidebar. + +Keep `dired' pointed at ROOT while cycling directories until +NAME is found in ROOT path. + +This is dependent on `dired-subtree-cycle'." + (let ((sidebar (dired-sidebar-buffer))) + (pop-to-buffer sidebar) + (when (and name + ;; Checking for a private method. *shrug* + (fboundp 'dired-subtree--is-expanded-p)) + (pop-to-buffer sidebar) + (goto-char 0) + (let* ((path root) + ;; Imagine root is /root/var/ and name is + ;; /root/var/a/b/c. + ;; This will return a list of '\("a" "b" "c"\). + (dirs (when (cadr (split-string name root)) + (split-string (cadr (split-string name root)) "/")))) + (dolist (dir dirs) + (let ((path-regex (concat "^.*[[:space:]]" (regexp-quote dir)))) + (setq path (concat path dir)) + (if (file-regular-p path) + ;; Try to use `dired-goto-file' to go to the correct + ;; file. If that fails, just search for the text. + (let ((default-directory (file-name-directory path))) + (unless (dired-goto-file path) + (condition-case nil + ;; It's hard to get this right so just using a + ;; heuristic will get 90% of the way there. + ;; Making sure there's a space in front of the name + ;; skips matches that contains the name as a + ;; substring which is probably good enough... + (re-search-forward path-regex) + ;; Sometimes `dired' gets out of sync with the file. + ;; Refresh the buffer and try the search again. + ;; One way to reproduce this: + ;; 1. Open file A as buffer B. + ;; 2. Delete file A in `dired'. + ;; 3. Hide `dired-sidebar'. + ;; 4. Save buffer B. + ;; 5. Re-open `dired-sidebar'. + (error + (revert-buffer) + (re-search-forward path-regex nil :no-error))))) + (re-search-forward path-regex) + ;; Check if subtree has already been expanded. + ;; Basically, we're using `dired-subtree-cycle' more + ;; like dired-subtree-expand. + (when (not (dired-subtree--is-expanded-p)) + ;; This will probably throw an error when trying to expand + ;; directories that have been collapsed by `dired-collapse'. + (dired-subtree-cycle)) + (setq path (concat path "/")))))) + (when dired-sidebar-recenter-cursor-on-follow-file + (recenter nil)) + (dired-sidebar-redisplay-icons)))) + +;;;###autoload +(defun dired-sidebar-toggle-with-current-directory () + "Like `dired-sidebar-toggle-sidebar' but use current-directory." + (interactive) + (let ((current-prefix-arg '(4))) ; C-u + (call-interactively #'dired-sidebar-toggle-sidebar))) + +;;;###autoload +(defun dired-sidebar-show-sidebar (&optional b) + "Show sidebar displaying buffer B." + (interactive) + (let ((buffer (or b + ;; Only expect this to be hit when called interactively. + (dired-sidebar-get-or-create-buffer + (dired-sidebar-get-dir-to-show))))) + (display-buffer-in-side-window buffer dired-sidebar-display-alist) + (let ((window (get-buffer-window buffer))) + (when dired-sidebar-no-delete-other-windows + (set-window-parameter window 'no-delete-other-windows t)) + (set-window-dedicated-p window t) + (with-selected-window window + (let ((window-size-fixed)) + (dired-sidebar-set-width dired-sidebar-width)))) + (with-current-buffer buffer + (if (eq major-mode 'dired-sidebar-mode) + (dired-build-subdir-alist) + (dired-sidebar-mode))) + (dired-sidebar-update-state buffer))) + +;;;###autoload +(defun dired-sidebar-hide-sidebar () + "Hide the sidebar in the selected frame." + (interactive) + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (delete-window (get-buffer-window buffer)) + (dired-sidebar-update-state nil))) + +;;;###autoload +(defun dired-sidebar-jump-to-sidebar () + "Jump to `dired-sidebar' buffer if it is showing. + +If it's not showing, act as `dired-sidebar-toggle-sidebar'." + (interactive) + (if (dired-sidebar-showing-sidebar-p) + (select-window + (get-buffer-window (dired-sidebar-buffer (selected-frame)))) + (call-interactively #'dired-sidebar-toggle-sidebar))) + +(defun dired-sidebar-find-file (&optional dir) + "Wrapper over `dired-find-file'. +Optional argument DIR Fine file using DIR of available. + +With prefix argument, use `dired-sidebar-alternate-select-window-function' for +window selection." + (interactive) + (let ((find-file-run-dired t) + (dired-file-name (or dir (dired-get-file-for-visit))) + (select-with-alt-window-function current-prefix-arg)) + (if (and (file-directory-p dired-file-name) + ;; For "." open a full-blown dired buffer, since the directory is + ;; already open in the sidebar. + (not (string= (file-name-nondirectory dired-file-name) + "."))) + (dired-sidebar-with-no-dedication + (let ((buf-name (dired-sidebar-buffer-name + dired-file-name))) + (if (dired-sidebar-buffer-exists-p buf-name) + (progn + (switch-to-buffer buf-name) + (dired-sidebar-update-state (current-buffer))) + (if (and dired-sidebar-one-instance-p (file-directory-p dired-file-name)) + (find-alternate-file dired-file-name) + ;; Copied from `dired-find-file'. + (find-file dired-file-name)) + (dired-sidebar-mode) + (dired-sidebar-update-state (current-buffer))))) + ;; Select the sidebar window so that `next-window' is consistent + ;; in picking the window next to the sidebar. + ;; This is useful for when `dired-sidebar-find-file' is called + ;; from a buffer that is not already in the sidebar buffer. + ;; e.g. A mouse click event. + (switch-to-buffer (dired-sidebar-buffer)) + (select-window + (if select-with-alt-window-function + (funcall dired-sidebar-alternate-select-window-function) + (if dired-sidebar-open-file-in-most-recently-used-window + (get-mru-window nil nil t) + (next-window)))) + (find-file dired-file-name) + (when dired-sidebar-close-sidebar-on-file-open + (dired-sidebar-hide-sidebar))))) + +(defun dired-sidebar-find-file-alt () + "Like `dired-sidebar-find-file' but select window with alterate method. + +Select alternate window using `dired-sidebar-alternate-select-window-function'." + (interactive) + (let ((current-prefix-arg '(4))) ; C-u + (call-interactively 'dired-sidebar-find-file))) + +(defun dired-sidebar-up-directory () + "Wrapper over `dired-up-directory'." + (interactive) + (dired-sidebar-with-no-dedication + ;; If `dired-subtree' is used, `dired-current-directory' is redefined. + ;; So move point to the top of the buffer to get the actual directory and + ;; not the one at point. + (when dired-sidebar-skip-subtree-parent + (goto-char (point-min))) + (let* ((dir (dired-current-directory)) + (up (file-name-directory (directory-file-name dir))) + (up-name (dired-sidebar-buffer-name up))) + (if (dired-sidebar-buffer-exists-p up-name) + (progn + (switch-to-buffer up-name) + (dired-sidebar-update-state (current-buffer))) + (if dired-sidebar-one-instance-p + (find-alternate-file "..") + (dired-up-directory)) + (dired-sidebar-mode) + (dired-sidebar-update-state (current-buffer))) + (let ((default-directory up)) + (dired-goto-file dir))))) + +(defun dired-sidebar-mouse-subtree-cycle-or-find-file (event) + "Handle a mouse click EVENT in `dired-sidebar'. + +For directories, if `dired-sidebar-cycle-subtree-on-click' is true, +cycle the directory. + +Otherwise, behaves the same as if user clicked on a file. + +For files, use `dired-sidebar-find-file'. + +This uses the same code as `dired-mouse-find-file-other-window' to find +the relevant file-directory clicked on by the mouse." + (interactive "e") + (let (window pos file) + (save-excursion + (setq window (posn-window (event-end event)) + pos (posn-point (event-end event))) + (if (not (windowp window)) + (error "No file chosen")) + (set-buffer (window-buffer window)) + (goto-char pos) + (setq file (dired-get-file-for-visit))) + ;; There's a flicker doing this but it doesn't seem like + ;; `dired-subtree-cycle' works without first selecting the window. + (with-selected-window window + (if (and dired-sidebar-cycle-subtree-on-click + (file-directory-p file) + (not (string-suffix-p "." file))) + (dired-subtree-cycle) + (dired-sidebar-find-file file))))) + +;; Helpers + +(defun dired-sidebar-buffer-exists-p (buffer-name) + "Check if a `dired-sidebar' buffer exists for BUFFER-NAME." + (get-buffer buffer-name)) + +(defun dired-sidebar-sidebar-root () + "Return directory using `projectile', `project' or current directory." + (if (featurep 'projectile) + (condition-case nil + (if (fboundp 'projectile-project-root) + (or (projectile-project-root) default-directory) + default-directory) + (error default-directory)) + ;; Use `project' if `projectile' is not loaded yet. + ;; `projectile' is a big package and takes a while to load so it's better + ;; to defer loading it as long as possible (until the user chooses). + (dired-sidebar-if-let* ((project (project-current))) + (cdr project) + default-directory))) + +(defun dired-sidebar-buffer-name (dir) + "Return name of `dired-sidebar' buffer given DIR." + (let ((b (cond + ((string-suffix-p ".." dir) + ;; ~/.emacs.d/elpa/.. -> ~/.emacs.d/ + (file-name-directory (substring dir 0 (- (length dir) 3)))) + ((not (string-suffix-p "/" dir)) + (concat dir "/")) + (:default + dir)))) + (concat ":" (abbreviate-file-name b)))) + +(defun dired-sidebar-get-or-create-buffer (root) + "Get or create a `dired-sidebar' buffer matching ROOT." + (interactive) + (let ((name (dired-sidebar-buffer-name root))) + (dired-sidebar-if-let* ((existing-buffer (get-buffer name))) + existing-buffer + (let ((buffer (dired-noselect root))) + ;; When opening a sidebar while in a dired buffer that matches + ;; the sidebar's root directory. + (if (eq (current-buffer) buffer) + ;; https://github.com/Fuco1/dired-hacks/issues/102 + (if (member 'dired-collapse-mode dired-mode-hook) + (progn + (remove-hook 'dired-mode-hook 'dired-collapse-mode) + (let ((clone (clone-buffer))) + (add-hook 'dired-mode-hook 'dired-collapse-mode) + clone)) + (clone-buffer)) + ;; Rename the buffer generated by `dired-noselect'. + (when (not (string-equal (buffer-name buffer) name)) + (with-current-buffer buffer + (rename-buffer name))) + buffer))))) + +(defun dired-sidebar-set-font () + "Customize font in `dired-sidebar'. + +Set font to a variable width (proportional) in the current buffer." + (interactive) + (setq-local buffer-face-mode-face dired-sidebar-face) + (buffer-face-mode)) + +(defun dired-sidebar-set-mode-line () + "Customize modeline in `dired-sidebar'." + (setq mode-line-format dired-sidebar-mode-line-format)) + +(defun dired-sidebar-set-width (width) + "Set the width of the buffer to WIDTH when it is created." + ;; Copied from `treemacs--set-width' as well as `neotree'. + (unless (one-window-p) + (let ((window-size-fixed) + (w (max width window-min-width))) + (cond + ((> (window-width) w) + (shrink-window-horizontally (- (window-width) w))) + ((< (window-width) w) + (enlarge-window-horizontally (- w (window-width)))))))) + +(defun dired-sidebar-update-buffer-name () + "Change buffer name to avoid collision with regular `dired' buffers." + (rename-buffer + (dired-sidebar-buffer-name (dired-current-directory)))) + +(defun dired-sidebar-update-state (buffer &optional f) + "Update current state with BUFFER for sidebar in F or selected frame." + (let ((frame (or f (selected-frame)))) + (if (assq frame dired-sidebar-alist) + (setcdr (assq frame dired-sidebar-alist) buffer) + (push `(,frame . ,buffer) dired-sidebar-alist)))) + +(defun dired-sidebar-showing-sidebar-p (&optional f) + "Whether F or selected frame is showing a sidebar. + +Check if F or selected frame contains a sidebar and return +corresponding buffer if buffer has a window attached to it. + +Return buffer if so." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer f))) + (get-buffer-window buffer))) + +(defun dired-sidebar-buffer (&optional f) + "Return the current sidebar buffer in F or selected frame. + +This can return nil if the buffer has been killed." + (let* ((frame (or f (selected-frame))) + (buffer (alist-get frame dired-sidebar-alist))) + ;; The buffer can be killed for a variety of reasons. + ;; This side effect is kind of messy but it's the simplest place + ;; to put the clean up code for `dired-sidebar-alist'. + (if (buffer-live-p buffer) + buffer + ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html + ;; Documentation for `assq-delete-all'. + ;; What kind of API is this?? :() + ;; Why does it only modify 'often' and not 'always'? ¯\_(ツ)_/¯ + ;; It returns the shortened alist, and often modifies the original list + ;; structure of alist. + ;; For correct results, use the return value of assq-delete-all rather + ;; than looking at the saved value of alist. + (setq dired-sidebar-alist + (assq-delete-all frame dired-sidebar-alist)) + nil))) + +(defun dired-sidebar-switch-to-dir (dir) + "Update buffer with DIR as root." + (when (dired-sidebar-showing-sidebar-p) + (let ((buffer (dired-sidebar-get-or-create-buffer dir))) + (dired-sidebar-show-sidebar buffer)))) + +(defun dired-sidebar-buffer-stale-p (&optional noconfirm) + "Wrapper over `dired-buffer-stale-p'. + +Check if buffer is stale only if `dired-sidebar-stale-buffer-time-idle-delay' + +has elapsed. + +Optional argument NOCONFIRM Pass NOCONFIRM on to `dired-buffer-stale-p'." + (when dired-sidebar-check-for-stale-buffer-p + (setq dired-sidebar-check-for-stale-buffer-p nil) + (dired-buffer-stale-p noconfirm))) + +(defun dired-sidebar-refresh-buffer (&rest _) + "Refresh sidebar buffer." + (dired-sidebar-when-let* ((sidebar (dired-sidebar-buffer))) + (with-current-buffer sidebar + (let ((auto-revert-verbose nil)) + (ignore auto-revert-verbose) ;; Make byte compiler happy. + (revert-buffer))))) + +(defun dired-sidebar-follow-file () + "Follow new file. + +The root of the sidebar will be determined by `dired-sidebar-get-dir-to-show' +and the file followed is will be determined by `dired-sidebar-get-file-to-show', + +both accounting for the currently selected window." + (when (dired-sidebar-showing-sidebar-p) + ;; Wrap in `with-selected-window' because we don't want to pop to + ;; the sidebar buffer. + ;; We also need to pick the correct selected-window so that + ;; `dired-sidebar-get-dir-to-show' can get the correct root to change to. + (with-selected-window (selected-window) + (let ((root (dired-sidebar-get-dir-to-show))) + (dired-sidebar-switch-to-dir root) + (when dired-sidebar-follow-file-at-point-on-toggle-open + (dired-sidebar-when-let* ((file (dired-sidebar-get-file-to-show))) + (dired-sidebar-point-at-file file root))))))) + +(defun dired-sidebar-default-alternate-select-window () + "Default function for `dired-sidebar-alternate-select-window-function'." + (if (fboundp 'aw-select) + (aw-select "Select Window") + (next-window))) + +(defun dired-sidebar-get-dir-to-show () + "Return the directory `dired-sidebar' should open to." + (expand-file-name + (cond + ((and (derived-mode-p 'magit-mode) + dired-sidebar-use-magit-integration + (fboundp 'magit-toplevel)) + (magit-toplevel)) + ((and (eq major-mode 'term-mode) + dired-sidebar-use-term-integration) + (dired-sidebar-term-get-pwd)) + ((and (eq major-mode 'dired-mode)) + default-directory) + ((and (eq major-mode 'ibuffer-mode) + (fboundp 'ibuffer-current-buffer) + (ibuffer-current-buffer)) + (let ((buffer-at-point (ibuffer-current-buffer))) + (if (fboundp 'ibuffer-projectile-root) + (dired-sidebar-if-let* ((ibuffer-projectile-root + (ibuffer-projectile-root buffer-at-point))) + (cdr ibuffer-projectile-root) + (with-current-buffer buffer-at-point + default-directory)) + (with-current-buffer buffer-at-point + default-directory)))) + (:default + (dired-sidebar-sidebar-root))))) + +(defun dired-sidebar-get-file-to-show () + "Return the file `dired-sidebar' should open to. + +This may return nil if there's no suitable file to show." + (cond + ((and dired-sidebar-use-magit-integration + (derived-mode-p 'magit-mode) + (fboundp 'magit-file-at-point) + (magit-file-at-point)) + (expand-file-name (magit-file-at-point))) + ((and (eq major-mode 'dired-mode)) + ;; Not sure if `dired-get-filename' is more appropriate. + (condition-case nil + (dired-get-file-for-visit) + (error nil))) + ((and (eq major-mode 'ibuffer-mode) + (fboundp 'ibuffer-current-buffer)) + (let ((bf-name (buffer-file-name (ibuffer-current-buffer)))) + (and bf-name (file-exists-p bf-name) bf-name))) + (:default + (and buffer-file-name (file-exists-p buffer-file-name) buffer-file-name)))) + +(defun dired-sidebar-term-get-pwd () + "Get current directory of `term-mode'. + +This is somewhat experimental/hacky." + (interactive) + (condition-case nil + (progn + (forward-paragraph) + (when (fboundp 'term-previous-prompt) + (term-previous-prompt 1)) + (when (fboundp 'term-simple-send) + (term-simple-send (get-buffer-process (current-buffer)) "pwd")) + (sleep-for 0 50) + (forward-line 1) + (let ((result (string-trim (thing-at-point 'line)))) + (kill-whole-line) + (forward-line -1) + (kill-whole-line) + result)) + (error + default-directory))) + +(defun dired-sidebar-subtree-toggle () + "Wrapper over `dired-subtree-toggle' that accounts for `all-the-icons-dired'." + (interactive) + (dired-subtree-toggle) + (dired-sidebar-redisplay-icons)) + +(defun dired-sidebar-redisplay-icons () + "Redisplay icon themes unless over TRAMP." + (unless (file-remote-p default-directory) + (when (and (eq dired-sidebar-theme 'icons) + (fboundp 'all-the-icons-dired--refresh)) + ;; Refresh `all-the-icons-dired'. + (dired-sidebar-revert) + (all-the-icons-dired--refresh)) + (when (dired-sidebar-using-tui-p) + (dired-sidebar-tui-update-with-delay)))) + +(defun dired-sidebar-advice-hide-temporarily (f &rest args) + "A function meant to be used with advice to temporarily hide itself. + +This function hides the sidebar before executing F and then reshows itself after." + (if (not (dired-sidebar-showing-sidebar-p)) + (apply f args) + (let ((sidebar (dired-sidebar-buffer))) + (dired-sidebar-hide-sidebar) + (apply f args) + (dired-sidebar-show-sidebar sidebar)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar-local dired-sidebar-tui-dired-displayed nil + "Flags whether icons have been added.") + +(defun dired-sidebar-tui-dired-reset (&optional _arg _noconfirm) + "Function used as advice when redisplaying buffer." + (setq-local dired-sidebar-tui-dired-displayed nil)) + +(defun dired-sidebar-tui-dired-display () + "Display the icons of files in a dired buffer." + (interactive) + (when (or t (and (not dired-sidebar-tui-dired-displayed) dired-subdir-alist)) + (setq-local dired-sidebar-tui-dired-displayed t) + (let ((inhibit-read-only t) + (collapsible-icon (if (eq dired-sidebar-theme 'nerd) "▾" "-")) + (expandable-icon (if (eq dired-sidebar-theme 'nerd) "▸" "+"))) + (save-excursion + (goto-char (point-min)) + (while (not (eobp)) + (when (dired-move-to-filename nil) + (dired-move-to-filename) + (let ((file (dired-get-filename 'verbatim t))) + (unless (member file '("." "..")) + (let ((filename (dired-get-filename nil t))) + (if (eq dired-sidebar-theme 'vscode) + (progn + (require 'vscode-icon) + (when (fboundp 'vscode-icon-for-file) + (insert-image + (vscode-icon-for-file filename) " ")) + (insert " ")) + (if (file-directory-p filename) + (if (dired-subtree--is-expanded-p) + (insert (concat collapsible-icon " ")) + (insert (concat expandable-icon " "))) + (insert ""))))))) + (forward-line 1)))))) + +(defun dired-sidebar-tui-update-with-delay (&rest _) + "Update tui interface after a delay." + (run-with-idle-timer + dired-sidebar-tui-update-delay nil + #'dired-sidebar-tui-update)) + +(defun dired-sidebar-tui-update () + "Workhorse function to update tui interface." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (with-current-buffer buffer + (dired-sidebar-revert) + (when dired-sidebar-recenter-cursor-on-tui-update + (recenter))))) + +(defun dired-sidebar-revert (&rest _) + "Wrapper around `dired-revert' but saves window position." + (dired-sidebar-when-let* ((window (get-buffer-window + (dired-sidebar-buffer)))) + (with-selected-window window + (let ((old-window-start (window-start))) + (when (dired-sidebar-using-tui-p) + (dired-sidebar-tui-reset-in-sidebar)) + (dired-revert) + (set-window-start window old-window-start))))) + +(defun dired-sidebar-tui-reset-in-sidebar (&rest _) + "Runs `dired-sidebar-tui-dired-reset' in current `dired-sidebar' buffer." + (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer))) + (with-current-buffer buffer + (dired-sidebar-tui-dired-reset)))) + +(defun dired-sidebar-setup-tui () + "Sets up text user interface for `dired-sidebar'. + +This is used in place of `all-the-icons' to add directory indicators. + +e.g. + and -." + (add-hook 'dired-after-readin-hook + 'dired-sidebar-tui-dired-display :append :local) + (setq-local dired-subtree-line-prefix " ") + (dired-build-subdir-alist) + (dired-sidebar-revert)) + +(defun dired-sidebar-using-tui-p () + "Return t if `dired-sidebar-theme' is using tui code path." + (or + (eq dired-sidebar-theme 'ascii) + (eq dired-sidebar-theme 'nerd) + (eq dired-sidebar-theme 'vscode))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defvar-local dired-sidebar-wdired-tracking-major-mode nil + "Track current `major-mode' when toggling to `wdired'.") + +(defun dired-sidebar-wdired-change-to-dired-mode-advice (f &rest args) + "Advice for `wdired-change-to-dired-mode'." + (if (eq dired-sidebar-wdired-tracking-major-mode 'dired-sidebar-mode) + (dired-sidebar-wdired-change-to-dired-mode) + (apply f args))) + +(defun dired-sidebar-wdired-change-to-dired-mode () + "Change the mode back to dired-sidebar. + +This is an exact copy of `wdired-change-to-dired-mode' but changes the +`major-mode' to `dired-sidebar-mode' instead of `dired-mode'." + (let ((inhibit-read-only t)) + (remove-text-properties + (point-min) (point-max) + '(front-sticky nil rear-nonsticky nil read-only nil keymap nil))) + (use-local-map dired-mode-map) + (force-mode-line-update) + (setq buffer-read-only t) + (setq major-mode 'dired-sidebar-mode) + (setq mode-name "Dired-sidebar") + (dired-advertise) + (remove-hook 'kill-buffer-hook 'wdired-check-kill-buffer t) + (set (make-local-variable 'revert-buffer-function) 'dired-sidebar-revert)) + +(defun dired-sidebar-wdired-change-to-wdired-mode-advice (f &rest args) + "Forward to `wdired-change-to-wdired-mode'. + +`wdired' expected the `major-mode' to be `dired-mode' first. + +Track the current `major-mode' and revert to that upon exiting `wdired'." + (setq dired-sidebar-wdired-tracking-major-mode major-mode) + (if (eq major-mode 'dired-mode) + (apply f args) + (let ((major-mode 'dired-mode)) + (apply f args)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(provide 'dired-sidebar) +;;; dired-sidebar.el ends here diff --git a/.emacs.d/plugins/flymake-yaml.el b/.emacs.d/plugins/flymake-yaml.el new file mode 100644 index 0000000..fe6aa5b --- /dev/null +++ b/.emacs.d/plugins/flymake-yaml.el @@ -0,0 +1,68 @@ +;;; flymake-yaml.el --- A flymake handler for YAML + +;; Copyright (C) 2013 Yasuyuki Oka + +;; Author: Yasuyuki Oka +;; Version: 0.0.2 +;; URL: https://github.com/yasuyk/flymake-yaml +;; Package-Requires: ((flymake-easy "0.1")) +;; Keywords: yaml + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; +;; Based in part on http://d.hatena.ne.jp/kitokitoki/20120306/p1 +;; +;; Usage: +;; +;; (require 'flymake-yaml) ;; Not necessary if using ELPA package +;; (add-hook 'yaml-mode-hook 'flymake-yaml-load) +;; +;; Uses flymake-easy, from https://github.com/purcell/flymake-easy + +;;; Code: + +(require 'flymake-easy) + +(defconst flymake-yaml-err-line-patterns + ;; Syck error message + '(("syntax error on line \\([0-9]+\\), col \\([0-9]+\\): `\\(.*\\)'" nil 1 2 3) + ;; Psych error message + (".*: \\(.*\\) at line \\([0-9]+\\) column \\([0-9]+\\)" nil 2 3 1))) + +(defun flymake-yaml-command (filename) + "Construct a command that flymake can use to check yaml source. +Argument FILENAME + YAML file name." + (list "ruby" "-ryaml" "-e" "YAML.load(ARGF) rescue warn $!" filename)) + +;;;###autoload +(defun flymake-yaml-load () + "Configure flymake mode to check the current buffer's YAML syntax." + (interactive) + (when (eq major-mode 'yaml-mode) + (flymake-easy-load 'flymake-yaml-command + flymake-yaml-err-line-patterns + 'tempdir + "yml"))) + +(provide 'flymake-yaml) + +;; Local Variables: +;; coding: utf-8 +;; eval: (checkdoc-minor-mode 1) +;; End: + +;;; flymake-yaml.el ends here diff --git a/.emacs.d/plugins/latexmk-mode.el b/.emacs.d/plugins/latexmk-mode.el new file mode 100644 index 0000000..e717fe5 --- /dev/null +++ b/.emacs.d/plugins/latexmk-mode.el @@ -0,0 +1,27 @@ +;;; latexmk-mode.el --- LatexMK minor mode +;;; Commentary: +;;; none +;;; Code: + +(define-minor-mode latexmk-mode + "Toggle LatexMK mode." + :init-value nil + :lighter " LatexMK " +) + +(defun my/run-latexmk () + (interactive) + (start-process "latexmk" "latexmk out" "latexmk" "--silent" "--pdf" (buffer-file-name (current-buffer))) +) + +(defun my/try-run-latexmk () + "Try to run latexmk." + + (if (bound-and-true-p latexmk-mode) + (my/run-latexmk) + ) + ) + +(add-hook 'after-save-hook 'my/try-run-latexmk) +(add-hook 'latex-mode-hook 'latexmk-mode) +;;; latexmk-mode.el ends here diff --git a/.emacs.d/plugins/livedown.el b/.emacs.d/plugins/livedown.el new file mode 100644 index 0000000..07762b8 --- /dev/null +++ b/.emacs.d/plugins/livedown.el @@ -0,0 +1,75 @@ +;;; livedown.el --- Realtime Markdown previews for Emacs. + +;; Copyright (C) 2014-2016 Hrvoje Simic + +;; Author: Hrvoje Simic +;; Version: 1.0.0 +;; Keywords: markdown, preview, live +;; URL: https://github.com/shime/emacs-livedown + +;;; Commentary: + +;; Realtime Markdown previews for Emacs. + +;;; Code: + +(defgroup livedown nil + "Realtime Markdown previews" + :group 'livedown + :prefix "livedown-") + +(defcustom livedown-port 1337 + "Port on which livedown server will run." + :type 'integer + :group 'livedown) + +(defcustom livedown-open t + "Open browser automatically." + :type 'boolean + :group 'livedown) + +(defcustom livedown-browser nil + "Open alternative browser." + :type 'string + :group 'livedown) + +(defcustom livedown-autostart nil + "Auto-open previews when opening markdown files." + :type 'boolean + :group 'livedown) + +;;;###autoload +(defun livedown-preview () + "Preview the current file in livedown." + (interactive) + + (call-process-shell-command + (format "livedown stop --port %s &" + livedown-port)) + + (start-process-shell-command + (format "emacs-livedown") + (format "emacs-livedown-buffer") + (format "livedown start %s --port %s %s %s " + buffer-file-name + livedown-port + (if livedown-browser (concat "--browser " livedown-browser) "") + (if livedown-open "--open" ""))) + (print (format "%s rendered @ %s" buffer-file-name livedown-port) (get-buffer "emacs-livedown-buffer"))) + +;;;###autoload +(defun livedown-kill (&optional async) + "Stops the livedown process." + (interactive) + (let ((stop-livedown (if async 'async-shell-command 'call-process-shell-command))) + (funcall stop-livedown + (format "livedown stop --port %s &" + livedown-port)))) + +(if livedown-autostart + (eval-after-load 'markdown-mode '(livedown-preview))) + +(add-hook 'kill-emacs-query-functions (lambda () (livedown-kill t))) + +(provide 'livedown) +;;; livedown.el ends here diff --git a/.emacs.d/plugins/org-ac.el b/.emacs.d/plugins/org-ac.el new file mode 100644 index 0000000..d34ff8c --- /dev/null +++ b/.emacs.d/plugins/org-ac.el @@ -0,0 +1,261 @@ +;;; org-ac.el --- Some auto-complete sources for org-mode + +;; Copyright (C) 2014 Hiroaki Otsu + +;; Author: Hiroaki Otsu +;; Keywords: org, completion +;; URL: https://github.com/aki2o/org-ac +;; Version: 0.0.2 +;; Package-Requires: ((auto-complete-pcmp "0.0.1") (log4e "0.2.0") (yaxception "0.1")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; +;; This extension provides auto-complete sources for org-mode. + +;;; Dependency: +;; +;; - auto-complete-pcmp.el ( see ) +;; - yaxception.el ( see ) +;; - log4e.el ( see ) + +;;; Installation: +;; +;; Put this to your load-path. +;; And put the following lines in your .emacs or site-start.el file. +;; +;; (require 'org-ac) + +;;; Configuration: +;; +;; ;; Make config suit for you. About the config item, see Customization or eval the following sexp. +;; ;; (customize-group "org-ac") +;; +;; (org-ac/config-default) + +;;; Customization: +;; +;; [EVAL] (autodoc-document-lisp-buffer :type 'user-variable :prefix "org-ac/" :docstring t) +;; `org-ac/ac-trigger-command-keys' +;; Keystrokes for doing `ac-start' with self insert. +;; +;; *** END auto-documentation + +;;; API: +;; +;; [EVAL] (autodoc-document-lisp-buffer :type 'command :prefix "org-ac/" :docstring t) +;; `org-ac/setup-current-buffer' +;; Do setup for using org-ac in current buffer. +;; +;; *** END auto-documentation +;; [Note] Functions and variables other than listed above, Those specifications may be changed without notice. + +;;; Tested On: +;; +;; - Emacs ... GNU Emacs 24.3.1 (i686-pc-linux-gnu, GTK+ Version 3.4.2) of 2013-08-22 on chindi02, modified by Debian +;; - auto-complete-pcmp.el ... Version 0.0.1 +;; - yaxception.el ... Version 0.1 +;; - log4e.el ... Version 0.2.0 + + +;; Enjoy!!! + + +(eval-when-compile (require 'cl)) +(require 'org) +(require 'auto-complete-pcmp) +(require 'rx) +(require 'log4e) +(require 'yaxception) + +(defgroup org-ac nil + "Auto completion for org-mode." + :group 'org + :prefix "org-ac/") + +(defcustom org-ac/ac-trigger-command-keys '("\\" "*" "SPC" ":" "[" "+") + "Keystrokes for doing `ac-start' with self insert." + :type '(repeat string) + :group 'org-ac) + + +(log4e:deflogger "org-ac" "%t [%l] %m" "%H:%M:%S" '((fatal . "fatal") + (error . "error") + (warn . "warn") + (info . "info") + (debug . "debug") + (trace . "trace"))) +(org-ac--log-set-level 'trace) + + +(defun* org-ac--show-message (msg &rest args) + (apply 'message (concat "[ORG-AC] " msg) args) + nil) + +(defun org-ac--complete-close-option-at-current-point () + (let ((pt (point))) + (yaxception:$ + (yaxception:try + (org-ac--trace "start complete close option at current point") + (when (save-excursion + (re-search-backward "#\\+\\(begin\\|BEGIN\\)_\\([a-zA-Z0-9]+\\) *\\=" nil t)) + (let* ((opennm (match-string-no-properties 1)) + (typenm (match-string-no-properties 2)) + (closenm (cond ((string= opennm "begin") "end") + ((string= opennm "BEGIN") "END"))) + (case-fold-search t)) + (if (or (not (re-search-forward "^[ \t]*#\\+" nil t)) + (not (re-search-forward (concat "\\=" closenm "_") nil t))) + (progn (goto-char pt) + (insert "\n#+" closenm "_" typenm) + (org-cycle)) + (let ((currtypenm (if (re-search-forward "\\=\\([a-zA-Z0-9]+\\)" nil t) + (match-string-no-properties 1) + ""))) + (backward-delete-char (+ (length closenm) + 1 + (length currtypenm))) + (insert closenm "_" typenm))) + (goto-char pt)))) + (yaxception:catch 'error e + (org-ac--show-message "Failed complete close option : %s" (yaxception:get-text e)) + (org-ac--error "failed complete close option at current point : %s\n%s" + (yaxception:get-text e) + (yaxception:get-stack-trace-string e)) + (goto-char pt))))) + +(defun org-ac--get-link-head-candidates () + (append (ac-pcmp/get-ac-candidates) + (mapcar (lambda (x) (concat x ":")) org-link-types))) + +(defvar ac-source-org-ac-tex + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "\\\\\\([a-zA-Z0-9_-]*\\)") + (symbol . "t") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar ac-source-org-ac-head + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "[^\r\n*]\\*\\([^\t\r\n]*\\)") + (symbol . "h") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar ac-source-org-ac-todo + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "^\\*+ \\([a-zA-Z0-9_-]*\\)") + (symbol . "d") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar ac-source-org-ac-tag + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "[ \t]:\\([a-zA-Z0-9_-]*\\)") + (symbol . "t") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar org-ac--regexp-link-head (rx-to-string `(and "[" + (* (any " \t")) + "[" + (group (* (not (any ":*]"))))))) +(defvar ac-source-org-ac-link-head + `((candidates . org-ac--get-link-head-candidates) + (prefix . ,org-ac--regexp-link-head) + (symbol . "l") + (requires . 0) + (cache) + (action . (lambda () + (ac-pcmp/do-ac-action) + (ac-start))))) + +(defvar ac-source-org-ac-option + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "^[ \t]*#\\+\\([a-zA-Z0-9_:=-]*\\)") + (symbol . "o") + (requires . 0) + (cache) + (action . (lambda () + (ac-pcmp/do-ac-action) + (org-ac--complete-close-option-at-current-point) + (auto-complete '(ac-source-org-ac-option-key)))))) + +(defvar ac-source-org-ac-option-key + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "^[ \t]*#\\+[a-zA-Z0-9_:=-]+ +\\([a-zA-Z0-9_-]*\\)") + (symbol . "k") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar ac-source-org-ac-option-options + '((candidates . ac-pcmp/get-ac-candidates) + (prefix . "^[ \t]*#\\+\\(?:options\\|OPTIONS\\):.* +\\([a-zA-Z0-9_-]*\\)") + (symbol . "x") + (requires . 0) + (cache) + (action . ac-pcmp/do-ac-action))) + +(defvar ac-source-org-ac-file + '((init . (setq ac-filename-cache nil)) + (candidates . org-ac/file-candidate) + (prefix . "\\[file:\\(.*\\)") + (symbol . "f") + (requires . 0) + (action . ac-start) + (limit . nil))) + + +;;;###autoload +(defun org-ac/setup-current-buffer () + "Do setup for using org-ac in current buffer." + (interactive) + (when (eq major-mode 'org-mode) + (loop for stroke in org-ac/ac-trigger-command-keys + do (local-set-key (read-kbd-macro stroke) 'ac-pcmp/self-insert-command-with-ac-start)) + (add-to-list 'ac-sources 'ac-source-org-ac-tex) + (add-to-list 'ac-sources 'ac-source-org-ac-head) + (add-to-list 'ac-sources 'ac-source-org-ac-todo) + (add-to-list 'ac-sources 'ac-source-org-ac-tag) + (add-to-list 'ac-sources 'ac-source-org-ac-link-head) + (add-to-list 'ac-sources 'ac-source-org-ac-option) + (add-to-list 'ac-sources 'ac-source-org-ac-option-key) + (add-to-list 'ac-sources 'ac-source-org-ac-option-options) + (add-to-list 'ac-sources 'ac-source-org-ac-file) + (auto-complete-mode t))) + +;;;###autoload +(defun org-ac/config-default () + "Do setting recommemded configuration." + (add-to-list 'ac-modes 'org-mode) + (add-hook 'org-mode-hook 'org-ac/setup-current-buffer t)) + + +(defun org-ac/file-candidate () + "Adds [file: to the normal file completition, plus allows relative paths" + (if (string-match "^[~./]+" ac-prefix) + (ac-filename-candidate) + (let ((ac-prefix (concat "./" ac-prefix))) + (mapcar (lambda (path) (substring path 2)) + (ac-filename-candidate))))) + + +(provide 'org-ac) +;;; org-ac.el ends here diff --git a/.emacs.d/plugins/smex.el b/.emacs.d/plugins/smex.el new file mode 100644 index 0000000..6d4a72b --- /dev/null +++ b/.emacs.d/plugins/smex.el @@ -0,0 +1,483 @@ +;;; smex.el --- M-x interface with Ido-style fuzzy matching. -*- lexical-binding: t; -*- + +;; Copyright (C) 2009-2014 Cornelius Mika and contributors +;; +;; Author: Cornelius Mika and contributors +;; URL: http://github.com/nonsequitur/smex/ +;; Package-Requires: ((emacs "24")) +;; Version: 3.0 +;; Keywords: convenience, usability + +;; This file is not part of GNU Emacs. + +;;; License: + +;; Licensed under the same terms as Emacs. + +;;; Commentary: + +;; Quick start: +;; run (smex-initialize) +;; +;; Bind the following commands: +;; smex, smex-major-mode-commands +;; +;; For a detailed introduction see: +;; http://github.com/nonsequitur/smex/blob/master/README.markdown + +;;; Code: + +(require 'ido) + +(defgroup smex nil + "M-x interface with Ido-style fuzzy matching and ranking heuristics." + :group 'extensions + :group 'convenience + :link '(emacs-library-link :tag "Lisp File" "smex.el")) + +(defcustom smex-auto-update t + "If non-nil, `Smex' checks for new commands each time it is run. +Turn it off for minor speed improvements on older systems." + :type 'boolean + :group 'smex) + +(defcustom smex-save-file (locate-user-emacs-file "smex-items" ".smex-items") + "File in which the smex state is saved between Emacs sessions. +Variables stored are: `smex-data', `smex-history'. +Must be set before initializing Smex." + :type 'string + :group 'smex) + +(defcustom smex-history-length 7 + "Determines on how many recently executed commands +Smex should keep a record. +Must be set before initializing Smex." + :type 'integer + :group 'smex) + +(defcustom smex-prompt-string "M-x " + "String to display in the Smex prompt." + :type 'string + :group 'smex) + +(defcustom smex-flex-matching t + "Enables Ido flex matching. On by default. +Set this to nil to disable fuzzy matching." + :type 'boolean + :group 'smex) + +(defvar smex-initialized-p nil) +(defvar smex-cache) +(defvar smex-ido-cache) +(defvar smex-data) +(defvar smex-history) +(defvar smex-command-count 0) +(defvar smex-custom-action nil) + +;; Check if Smex is supported +(when (equal (cons 1 1) + (ignore-errors + (subr-arity (symbol-function 'execute-extended-command)))) + (error "Your Emacs has a non-elisp version of `execute-extended-command', which is incompatible with Smex")) + +;;-------------------------------------------------------------------------------- +;; Smex Interface + +;;;###autoload +(defun smex () + (interactive) + (unless smex-initialized-p + (smex-initialize)) + (if (smex-already-running) + (smex-update-and-rerun) + (and smex-auto-update + (smex-detect-new-commands) + (smex-update)) + (smex-read-and-run smex-ido-cache))) + +(defun smex-already-running () + (and (boundp 'ido-choice-list) + (eql ido-choice-list smex-ido-cache) + (minibuffer-window-active-p (selected-window)))) + +(defun smex-update-and-rerun () + (smex-do-with-selected-item + (lambda (_) (smex-update) (smex-read-and-run smex-ido-cache ido-text)))) + +(defun smex-read-and-run (commands &optional initial-input) + (let* ((chosen-item-name (smex-completing-read commands initial-input)) + (chosen-item (intern chosen-item-name))) + (if smex-custom-action + (let ((action smex-custom-action)) + (setq smex-custom-action nil) + (funcall action chosen-item)) + (unwind-protect + (with-no-warnings ; Don't warn about interactive use of `execute-extended-command' + (execute-extended-command current-prefix-arg chosen-item-name)) + (smex-rank chosen-item))))) + +;;;###autoload +(defun smex-major-mode-commands () + "Like `smex', but limited to commands that are relevant to the active major mode." + (interactive) + (unless smex-initialized-p + (smex-initialize)) + (let ((commands (delete-dups (append (smex-extract-commands-from-keymap (current-local-map)) + (smex-extract-commands-from-features major-mode))))) + (setq commands (smex-sort-according-to-cache commands)) + (setq commands (mapcar #'symbol-name commands)) + (smex-read-and-run commands))) + +(defun smex-completing-read (choices initial-input) + (let ((ido-completion-map ido-completion-map) + (ido-setup-hook (cons 'smex-prepare-ido-bindings ido-setup-hook)) + (ido-enable-prefix nil) + (ido-enable-flex-matching smex-flex-matching) + (ido-max-prospects 10) + (minibuffer-completion-table choices)) + (ido-completing-read (smex-prompt-with-prefix-arg) choices nil nil + initial-input 'extended-command-history (car choices)))) + +(defun smex-prompt-with-prefix-arg () + (if (not current-prefix-arg) + smex-prompt-string + (concat + (if (eq current-prefix-arg '-) + "- " + (if (integerp current-prefix-arg) + (format "%d " current-prefix-arg) + (if (= (car current-prefix-arg) 4) + "C-u " + (format "%d " (car current-prefix-arg))))) + smex-prompt-string))) + +(defun smex-prepare-ido-bindings () + (define-key ido-completion-map (kbd "TAB") 'minibuffer-complete) + (define-key ido-completion-map (kbd "C-h f") 'smex-describe-function) + (define-key ido-completion-map (kbd "C-h w") 'smex-where-is) + (define-key ido-completion-map (kbd "M-.") 'smex-find-function) + (define-key ido-completion-map (kbd "C-a") 'move-beginning-of-line)) + +;;-------------------------------------------------------------------------------- +;; Cache and Maintenance + +(defun smex-rebuild-cache () + (interactive) + (setq smex-cache nil) + + ;; Build up list 'new-commands' and later put it at the end of 'smex-cache'. + ;; This speeds up sorting. + (let (new-commands) + (mapatoms (lambda (symbol) + (when (commandp symbol) + (let ((known-command (assq symbol smex-data))) + (if known-command + (setq smex-cache (cons known-command smex-cache)) + (setq new-commands (cons (list symbol) new-commands))))))) + (if (eq (length smex-cache) 0) + (setq smex-cache new-commands) + (setcdr (last smex-cache) new-commands))) + + (setq smex-cache (sort smex-cache 'smex-sorting-rules)) + (smex-restore-history) + (setq smex-ido-cache (smex-convert-for-ido smex-cache))) + +(defun smex-convert-for-ido (command-items) + (mapcar (lambda (command-item) (symbol-name (car command-item))) command-items)) + +(defun smex-restore-history () + "Rearranges `smex-cache' according to `smex-history'" + (if (> (length smex-history) smex-history-length) + (setcdr (nthcdr (- smex-history-length 1) smex-history) nil)) + (mapc (lambda (command) + (unless (eq command (caar smex-cache)) + (let ((command-cell-position (smex-detect-position + smex-cache + (lambda (cell) + (eq command (caar cell)))))) + (when command-cell-position + (let ((command-cell (smex-remove-nth-cell + command-cell-position smex-cache))) + (setcdr command-cell smex-cache) + (setq smex-cache command-cell)))))) + (reverse smex-history))) + +(defun smex-sort-according-to-cache (list) + "Sorts a list of commands by their order in `smex-cache'" + (let (sorted) + (dolist (command-item smex-cache) + (let ((command (car command-item))) + (when (memq command list) + (setq sorted (cons command sorted)) + (setq list (delq command list))))) + (nreverse (append list sorted)))) + +(defun smex-update () + (interactive) + (smex-save-history) + (smex-rebuild-cache)) + +(defun smex-detect-new-commands () + (let ((i 0)) + (mapatoms (lambda (symbol) (if (commandp symbol) (setq i (1+ i))))) + (unless (= i smex-command-count) + (setq smex-command-count i)))) + +(defun smex-auto-update (&optional idle-time) + "Update Smex when Emacs has been idle for IDLE-TIME." + (unless idle-time (setq idle-time 60)) + (run-with-idle-timer idle-time t + '(lambda () (if (smex-detect-new-commands) (smex-update))))) + +;;;###autoload +(defun smex-initialize () + (interactive) + (unless ido-mode (smex-initialize-ido)) + (smex-load-save-file) + (smex-detect-new-commands) + (smex-rebuild-cache) + (add-hook 'kill-emacs-hook 'smex-save-to-file) + (setq smex-initialized-p t)) + +(defun smex-initialize-ido () + "Sets up a minimal Ido environment for `ido-completing-read'." + (with-no-warnings ; `ido-init-completion-maps' is deprecated in Emacs 25 + (ido-init-completion-maps)) + (add-hook 'minibuffer-setup-hook 'ido-minibuffer-setup)) + +(defsubst smex-save-file-not-empty-p () + (string-match-p "\[^[:space:]\]" (buffer-string))) + +(defun smex-load-save-file () + "Loads `smex-history' and `smex-data' from `smex-save-file'" + (let ((save-file (expand-file-name smex-save-file))) + (if (file-readable-p save-file) + (with-temp-buffer + (insert-file-contents save-file) + (condition-case nil + (setq smex-history (read (current-buffer)) + smex-data (read (current-buffer))) + (error (if (smex-save-file-not-empty-p) + (error "Invalid data in smex-save-file (%s). Can't restore history." + smex-save-file) + (unless (boundp 'smex-history) (setq smex-history nil)) + (unless (boundp 'smex-data) (setq smex-data nil)))))) + (setq smex-history nil smex-data nil)))) + +(defun smex-save-history () + "Updates `smex-history'" + (setq smex-history nil) + (let ((cell smex-cache)) + (dotimes (_ smex-history-length) + (setq smex-history (cons (caar cell) smex-history)) + (setq cell (cdr cell)))) + (setq smex-history (nreverse smex-history))) + +(defmacro smex-pp (list-var) + `(smex-pp* ,list-var ,(symbol-name list-var))) + +(defun smex-save-to-file () + (interactive) + (smex-save-history) + (with-temp-file (expand-file-name smex-save-file) + (smex-pp smex-history) + (smex-pp smex-data))) + +;;-------------------------------------------------------------------------------- +;; Ranking + +(defun smex-sorting-rules (command-item other-command-item) + "Returns true if COMMAND-ITEM should sort before OTHER-COMMAND-ITEM." + (let* ((count (or (cdr command-item ) 0)) + (other-count (or (cdr other-command-item) 0)) + (name (car command-item)) + (other-name (car other-command-item)) + (length (length (symbol-name name))) + (other-length (length (symbol-name other-name)))) + (or (> count other-count) ; 1. Frequency of use + (and (= count other-count) + (or (< length other-length) ; 2. Command length + (and (= length other-length) + (string< name other-name))))))) ; 3. Alphabetical order + +(defun smex-rank (command) + (let ((command-item (or (assq command smex-cache) + ;; Update caches and try again if not found. + (progn (smex-update) + (assq command smex-cache))))) + (when command-item + (smex-update-counter command-item) + + ;; Don't touch the cache order if the chosen command + ;; has just been execucted previously. + (unless (eq command-item (car smex-cache)) + (let (command-cell + (pos (smex-detect-position smex-cache (lambda (cell) + (eq command-item (car cell)))))) + ;; Remove the just executed command. + (setq command-cell (smex-remove-nth-cell pos smex-cache)) + ;; And put it on top of the cache. + (setcdr command-cell smex-cache) + (setq smex-cache command-cell) + + ;; Repeat the same for the ido cache. Should this be DRYed? + (setq command-cell (smex-remove-nth-cell pos smex-ido-cache)) + (setcdr command-cell smex-ido-cache) + (setq smex-ido-cache command-cell) + + ;; Now put the last history item back to its normal place. + (smex-sort-item-at smex-history-length)))))) + +(defun smex-update-counter (command-item) + (let ((count (cdr command-item))) + (setcdr command-item + (if count + (1+ count) + ;; Else: Command has just been executed for the first time. + ;; Add it to `smex-data'. + (if smex-data + (setcdr (last smex-data) (list command-item)) + (setq smex-data (list command-item))) + 1)))) + +(defun smex-sort-item-at (n) + "Sorts item at position N in `smex-cache'." + (let* ((command-cell (nthcdr n smex-cache)) + (command-item (car command-cell))) + (let ((insert-at (smex-detect-position + command-cell + (lambda (cell) + (smex-sorting-rules command-item (car cell)))))) + ;; TODO: Should we handle the case of 'insert-at' being nil? + ;; This will never happen in practice. + (when (> insert-at 1) + (setq command-cell (smex-remove-nth-cell n smex-cache)) + ;; smex-cache just got shorter by one element, so subtract '1' from insert-at. + (setq insert-at (+ n (- insert-at 1))) + (smex-insert-cell command-cell insert-at smex-cache) + + ;; Repeat the same for the ido cache. DRY? + (setq command-cell (smex-remove-nth-cell n smex-ido-cache)) + (smex-insert-cell command-cell insert-at smex-ido-cache))))) + +(defun smex-detect-position (cell function) + "Detects, relatively to CELL, the position of the cell +on which FUNCTION returns true. +Only checks cells after CELL, starting with the cell right after CELL. +Returns nil when reaching the end of the list." + (let ((pos 1)) + (catch 'break + (while t + (setq cell (cdr cell)) + (if (not cell) + (throw 'break nil) + (if (funcall function cell) (throw 'break pos)) + (setq pos (1+ pos))))))) + +(defun smex-remove-nth-cell (n list) + "Removes and returns the Nth cell in LIST." + (let* ((previous-cell (nthcdr (- n 1) list)) + (result (cdr previous-cell))) + (setcdr previous-cell (cdr result)) + result)) + +(defun smex-insert-cell (new-cell n list) + "Inserts cell at position N in LIST." + (let* ((cell (nthcdr (- n 1) list)) + (next-cell (cdr cell))) + (setcdr (setcdr cell new-cell) next-cell))) + +;;-------------------------------------------------------------------------------- +;; Help and Reference + +(defun smex-do-with-selected-item (fn) + (setq smex-custom-action fn) + (ido-exit-minibuffer)) + +(defun smex-describe-function () + (interactive) + (smex-do-with-selected-item (lambda (chosen) + (describe-function chosen) + (pop-to-buffer "*Help*")))) + +(defun smex-where-is () + (interactive) + (smex-do-with-selected-item 'where-is)) + +(defun smex-find-function () + (interactive) + (smex-do-with-selected-item 'find-function)) + +(defun smex-extract-commands-from-keymap (map) + (let (commands) + (smex-parse-keymap map commands) + commands)) + +(defun smex-parse-keymap (map commands) + (map-keymap (lambda (_binding element) + (if (and (listp element) (eq 'keymap (car element))) + (smex-parse-keymap element commands) + ;; Strings are commands, too. Reject them. + (if (and (symbolp element) (commandp element)) + (push element commands)))) + map)) + +(defun smex-extract-commands-from-features (mode) + (let ((library-path (symbol-file mode)) + (mode-name (symbol-name mode)) + commands) + + (string-match "\\(.+?\\)\\(-mode\\)?$" mode-name) + ;; 'lisp-mode' -> 'lisp' + (setq mode-name (match-string 1 mode-name)) + (if (string= mode-name "c") (setq mode-name "cc")) + (setq mode-name (regexp-quote mode-name)) + + (dolist (feature load-history) + (let ((feature-path (car feature))) + (when (and feature-path (or (equal feature-path library-path) + (string-match mode-name (file-name-nondirectory + feature-path)))) + (dolist (item (cdr feature)) + (if (and (listp item) (eq 'defun (car item))) + (let ((function (cdr item))) + (when (commandp function) + (setq commands (append commands (list function)))))))))) + commands)) + +(defun smex-show-unbound-commands () + "Shows unbound commands in a new buffer, +sorted by frequency of use." + (interactive) + (setq smex-data (sort smex-data 'smex-sorting-rules)) + (let ((unbound-commands (delq nil + (mapcar (lambda (command-item) + (unless (where-is-internal (car command-item)) + command-item)) + smex-data)))) + (view-buffer-other-window "*Smex: Unbound Commands*") + (setq buffer-read-only t) + (let ((inhibit-read-only t)) + (erase-buffer) + (smex-pp unbound-commands)) + (set-buffer-modified-p nil) + (goto-char (point-min)))) + +;; A copy of `ido-pp' that's compatible with lexical bindings +(defun smex-pp* (list list-name) + (let ((print-level nil) (eval-expression-print-level nil) + (print-length nil) (eval-expression-print-length nil)) + (insert "\n;; ----- " list-name " -----\n(\n ") + (while list + (let* ((elt (car list)) + (s (if (consp elt) (car elt) elt))) + (if (and (stringp s) (= (length s) 0)) + (setq s nil)) + (if s + (prin1 elt (current-buffer))) + (if (and (setq list (cdr list)) s) + (insert "\n ")))) + (insert "\n)\n"))) + +(provide 'smex) +;;; smex.el ends here diff --git a/.emacs.d/smex-items b/.emacs.d/smex-items new file mode 100644 index 0000000..9ceb053 --- /dev/null +++ b/.emacs.d/smex-items @@ -0,0 +1,42 @@ + +;; ----- smex-history ----- +( + goto-line + overwrite-mode + shell + load-theme + treemacs + inf-ruby-console-auto + compile +) + +;; ----- smex-data ----- +( + (lsp-install-server . 6) + (treemacs . 17) + (lsp-java-update-server . 1) + (set-frame-font . 1) + (lsp-jt-browser . 6) + (sort-lines . 5) + (lsp-jt-report-open . 17) + (treemacs-edit-workspaces . 2) + (treemacs-refresh . 1) + (treemacs-mode . 3) + (menu-set-font . 2) + (magit-commit . 1) + (column-number-mode . 1) + (set-fill-column . 3) + (dap-java-debug . 7) + (ruby-indent-exp . 1) + (ruby-indent-line . 1) + (ruby-mode-menu . 1) + (inf-ruby-console-auto . 8) + (shell . 4) + (package-install-selected-packages . 1) + (package-install . 1) + (compile . 2) + (lsp-mode . 2) + (load-theme . 14) + (overwrite-mode . 1) + (goto-line . 2) +) diff --git a/.emacs.d/tramp b/.emacs.d/tramp new file mode 100644 index 0000000..76983ed --- /dev/null +++ b/.emacs.d/tramp @@ -0,0 +1,6 @@ +;; -*- emacs-lisp -*- <21/03/22 00:03:53 /home/yorune/.emacs.d/tramp> +;; Tramp connection history. Don't change this file. +;; Run `M-x tramp-cleanup-all-connections' instead. + +(((tramp-file-name "sudo" "root" nil "Gentoo" nil nil nil) + nil)) diff --git a/.emacs.d/transient/history.el b/.emacs.d/transient/history.el new file mode 100644 index 0000000..ef0bc18 --- /dev/null +++ b/.emacs.d/transient/history.el @@ -0,0 +1,3 @@ +((magit-commit nil) + (magit-dispatch nil) + (magit-push nil)) diff --git a/.local/bin/Checking-repo b/.local/bin/Checking-repo new file mode 100755 index 0000000..d50ca53 --- /dev/null +++ b/.local/bin/Checking-repo @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +#################################################### +# +# SRC_PREPARE +# +# Marcin Woźniak +# y0rune@aol.com +# +# Last edit: 16-09-2020 +# +################################################### + +mainteiner="Marcin Woźniak" + +function scanning(){ + EUSCAN=$(euscan --nocolor --quiet "$1") + if [ -n "$EUSCAN" ]; then + echo $1 + echo "=============== NOW: $(find ./* -mindepth 2 -maxdepth 2 -name ''"$1"'*.ebuild' | sort | tail -1) ================= + $(echo -e "$EUSCAN" | tail -1)" + sleep 1 + fi +} + +function folder(){ + cd "$1"; git pull || exit + + PACKAGES=() + + for FILE in */* + do + PACKAGE=$(echo "$FILE" | grep -Eo '[A-z0-9_-]+$') + PACKAGES+=("$PACKAGE") + done + + for i in "${PACKAGES[@]}" + do + scanning "$i" & + done + + for j in $(jobs -p) + do + wait "$j" + done + + echo -n ">>> Done scanning $1" +} + +function nofolder(){ + if [ -z "${1}" ]; then + echo "No overlay names given" + echo "Please give at least one overlay name as a commandline argument" + echo "Exiting" + exit 1 + fi + +for overlay in "${@}" +do + for ebuild in $(EIX_LIMIT=0 eix --only-names --in-overlay "${overlay}") + do + euscan --nocolor --quiet "${ebuild}" & + sleep 1 + done + + for j in $(jobs -p) + do + wait "$j" + done + + echo -n ">>> Done scanning ${overlay}" +done +} + +function help(){ + echo "You can use:" + echo "* -r or --repo " + echo "* -f or --folder " + echo + echo "Example of usage" + echo "./logeuscan -r src_prepare-overlay" + echo "./logeuscan -f ~/git/src_prepare-overlay" +} + +function main(){ + [ "$(whereis eix | wc -w)" -le "1" ] && { echo "The eix is NOT installed"; exit; } + [ "$(whereis euscan | wc -w)" -le "1" ] && { echo "The euscan is NOT installed"; exit; } + case $1 in + -h|--help) + help + ;; + -r|--repo) + nofolder "$2" | tee -a "euscan-$(date -I).log" + ;; + -f|--folder) + folder "$2" | tee -a "euscan-$(date -I).log" + + ;; + *) + echo "No found variable"; echo; help + esac +} + +main "$@" diff --git a/.local/bin/Logs b/.local/bin/Logs new file mode 120000 index 0000000..554b400 --- /dev/null +++ b/.local/bin/Logs @@ -0,0 +1 @@ +emerge-logs \ No newline at end of file diff --git a/.local/bin/__pycache__/pwiz.cpython-38.pyc b/.local/bin/__pycache__/pwiz.cpython-38.pyc new file mode 100644 index 0000000..a1974fc Binary files /dev/null and b/.local/bin/__pycache__/pwiz.cpython-38.pyc differ diff --git a/.local/bin/ansible b/.local/bin/ansible new file mode 100755 index 0000000..5d48dfe --- /dev/null +++ b/.local/bin/ansible @@ -0,0 +1,177 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# (c) 2012, Michael DeHaan +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# PYTHON_ARGCOMPLETE_OK + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +__requires__ = ['ansible_core'] + + +import errno +import os +import shutil +import sys +import traceback + +from ansible import context +from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError +from ansible.module_utils._text import to_text + + +# Used for determining if the system is running a new enough python version +# and should only restrict on our documented minimum versions +_PY38_MIN = sys.version_info[:2] >= (3, 8) +_PY3_MIN = sys.version_info[:2] >= (3, 5) +_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,) +_PY_MIN = _PY3_MIN or _PY2_MIN +if not _PY_MIN: + raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines())) + + +class LastResort(object): + # OUTPUT OF LAST RESORT + def display(self, msg, log_only=None): + print(msg, file=sys.stderr) + + def error(self, msg, wrap_text=None): + print(msg, file=sys.stderr) + + +if __name__ == '__main__': + + display = LastResort() + + try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace + import ansible.constants as C + from ansible.utils.display import Display, initialize_locale + except AnsibleOptionsError as e: + display.error(to_text(e), wrap_text=False) + sys.exit(5) + + initialize_locale() + + cli = None + me = os.path.basename(sys.argv[0]) + + try: + display = Display() + if C.CONTROLLER_PYTHON_WARNING and not _PY38_MIN: + display.deprecated( + ( + 'Ansible will require Python 3.8 or newer on the controller starting with Ansible 2.12. ' + 'Current version: %s' % ''.join(sys.version.splitlines()) + ), + version='2.12', + collection_name='ansible.builtin', + ) + display.debug("starting run") + + sub = None + target = me.split('-') + if target[-1][0].isdigit(): + # Remove any version or python version info as downstreams + # sometimes add that + target = target[:-1] + + if len(target) > 1: + sub = target[1] + myclass = "%sCLI" % sub.capitalize() + elif target[0] == 'ansible': + sub = 'adhoc' + myclass = 'AdHocCLI' + else: + raise AnsibleError("Unknown Ansible alias: %s" % me) + + try: + mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass) + except ImportError as e: + # ImportError members have changed in py3 + if 'msg' in dir(e): + msg = e.msg + else: + msg = e.message + if msg.endswith(' %s' % sub): + raise AnsibleError("Ansible sub-program not implemented: %s" % me) + else: + raise + + b_ansible_dir = os.path.expanduser(os.path.expandvars(b"~/.ansible")) + try: + os.mkdir(b_ansible_dir, 0o700) + except OSError as exc: + if exc.errno != errno.EEXIST: + display.warning("Failed to create the directory '%s': %s" + % (to_text(b_ansible_dir, errors='surrogate_or_replace'), + to_text(exc, errors='surrogate_or_replace'))) + else: + display.debug("Created the '%s' directory" % to_text(b_ansible_dir, errors='surrogate_or_replace')) + + try: + args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv] + except UnicodeError: + display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8') + display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc())) + exit_code = 6 + else: + cli = mycli(args) + exit_code = cli.run() + + except AnsibleOptionsError as e: + cli.parser.print_help() + display.error(to_text(e), wrap_text=False) + exit_code = 5 + except AnsibleParserError as e: + display.error(to_text(e), wrap_text=False) + exit_code = 4 +# TQM takes care of these, but leaving comment to reserve the exit codes +# except AnsibleHostUnreachable as e: +# display.error(str(e)) +# exit_code = 3 +# except AnsibleHostFailed as e: +# display.error(str(e)) +# exit_code = 2 + except AnsibleError as e: + display.error(to_text(e), wrap_text=False) + exit_code = 1 + except KeyboardInterrupt: + display.error("User interrupted execution") + exit_code = 99 + except Exception as e: + if C.DEFAULT_DEBUG: + # Show raw stacktraces in debug mode, It also allow pdb to + # enter post mortem mode. + raise + have_cli_options = bool(context.CLIARGS) + display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False) + if not have_cli_options or have_cli_options and context.CLIARGS['verbosity'] > 2: + log_only = False + if hasattr(e, 'orig_exc'): + display.vvv('\nexception type: %s' % to_text(type(e.orig_exc))) + why = to_text(e.orig_exc) + if to_text(e) != why: + display.vvv('\noriginal msg: %s' % why) + else: + display.display("to see the full traceback, use -vvv") + log_only = True + display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only) + exit_code = 250 + + sys.exit(exit_code) diff --git a/.local/bin/ansible-config b/.local/bin/ansible-config new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-config @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-connection b/.local/bin/ansible-connection new file mode 100755 index 0000000..2dd431c --- /dev/null +++ b/.local/bin/ansible-connection @@ -0,0 +1,342 @@ +#!/usr/bin/python3 +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +__requires__ = ['ansible_core'] + + +import fcntl +import hashlib +import os +import signal +import socket +import sys +import time +import traceback +import errno +import json + +from contextlib import contextmanager + +from ansible import constants as C +from ansible.module_utils._text import to_bytes, to_text +from ansible.module_utils.six import PY3 +from ansible.module_utils.six.moves import cPickle, StringIO +from ansible.module_utils.connection import Connection, ConnectionError, send_data, recv_data +from ansible.module_utils.service import fork_process +from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder +from ansible.playbook.play_context import PlayContext +from ansible.plugins.loader import connection_loader +from ansible.utils.path import unfrackpath, makedirs_safe +from ansible.utils.display import Display +from ansible.utils.jsonrpc import JsonRpcServer + + +def read_stream(byte_stream): + size = int(byte_stream.readline().strip()) + + data = byte_stream.read(size) + if len(data) < size: + raise Exception("EOF found before data was complete") + + data_hash = to_text(byte_stream.readline().strip()) + if data_hash != hashlib.sha1(data).hexdigest(): + raise Exception("Read {0} bytes, but data did not match checksum".format(size)) + + # restore escaped loose \r characters + data = data.replace(br'\r', b'\r') + + return data + + +@contextmanager +def file_lock(lock_path): + """ + Uses contextmanager to create and release a file lock based on the + given path. This allows us to create locks using `with file_lock()` + to prevent deadlocks related to failure to unlock properly. + """ + + lock_fd = os.open(lock_path, os.O_RDWR | os.O_CREAT, 0o600) + fcntl.lockf(lock_fd, fcntl.LOCK_EX) + yield + fcntl.lockf(lock_fd, fcntl.LOCK_UN) + os.close(lock_fd) + + +class ConnectionProcess(object): + ''' + The connection process wraps around a Connection object that manages + the connection to a remote device that persists over the playbook + ''' + def __init__(self, fd, play_context, socket_path, original_path, task_uuid=None, ansible_playbook_pid=None): + self.play_context = play_context + self.socket_path = socket_path + self.original_path = original_path + self._task_uuid = task_uuid + + self.fd = fd + self.exception = None + + self.srv = JsonRpcServer() + self.sock = None + + self.connection = None + self._ansible_playbook_pid = ansible_playbook_pid + + def start(self, variables): + try: + messages = list() + result = {} + + messages.append(('vvvv', 'control socket path is %s' % self.socket_path)) + + # If this is a relative path (~ gets expanded later) then plug the + # key's path on to the directory we originally came from, so we can + # find it now that our cwd is / + if self.play_context.private_key_file and self.play_context.private_key_file[0] not in '~/': + self.play_context.private_key_file = os.path.join(self.original_path, self.play_context.private_key_file) + self.connection = connection_loader.get(self.play_context.connection, self.play_context, '/dev/null', + task_uuid=self._task_uuid, ansible_playbook_pid=self._ansible_playbook_pid) + self.connection.set_options(var_options=variables) + + self.connection._socket_path = self.socket_path + self.srv.register(self.connection) + messages.extend([('vvvv', msg) for msg in sys.stdout.getvalue().splitlines()]) + + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.bind(self.socket_path) + self.sock.listen(1) + messages.append(('vvvv', 'local domain socket listeners started successfully')) + except Exception as exc: + messages.extend(self.connection.pop_messages()) + result['error'] = to_text(exc) + result['exception'] = traceback.format_exc() + finally: + result['messages'] = messages + self.fd.write(json.dumps(result, cls=AnsibleJSONEncoder)) + self.fd.close() + + def run(self): + try: + while not self.connection._conn_closed: + signal.signal(signal.SIGALRM, self.connect_timeout) + signal.signal(signal.SIGTERM, self.handler) + signal.alarm(self.connection.get_option('persistent_connect_timeout')) + + self.exception = None + (s, addr) = self.sock.accept() + signal.alarm(0) + signal.signal(signal.SIGALRM, self.command_timeout) + while True: + data = recv_data(s) + if not data: + break + log_messages = self.connection.get_option('persistent_log_messages') + + if log_messages: + display.display("jsonrpc request: %s" % data, log_only=True) + + request = json.loads(to_text(data, errors='surrogate_or_strict')) + if request.get('method') == "exec_command" and not self.connection.connected: + self.connection._connect() + + signal.alarm(self.connection.get_option('persistent_command_timeout')) + + resp = self.srv.handle_request(data) + signal.alarm(0) + + if log_messages: + display.display("jsonrpc response: %s" % resp, log_only=True) + + send_data(s, to_bytes(resp)) + + s.close() + + except Exception as e: + # socket.accept() will raise EINTR if the socket.close() is called + if hasattr(e, 'errno'): + if e.errno != errno.EINTR: + self.exception = traceback.format_exc() + else: + self.exception = traceback.format_exc() + + finally: + # allow time for any exception msg send over socket to receive at other end before shutting down + time.sleep(0.1) + + # when done, close the connection properly and cleanup the socket file so it can be recreated + self.shutdown() + + def connect_timeout(self, signum, frame): + msg = 'persistent connection idle timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and ' \ + 'Troubleshooting Guide.' % self.connection.get_option('persistent_connect_timeout') + display.display(msg, log_only=True) + raise Exception(msg) + + def command_timeout(self, signum, frame): + msg = 'command timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide.'\ + % self.connection.get_option('persistent_command_timeout') + display.display(msg, log_only=True) + raise Exception(msg) + + def handler(self, signum, frame): + msg = 'signal handler called with signal %s.' % signum + display.display(msg, log_only=True) + raise Exception(msg) + + def shutdown(self): + """ Shuts down the local domain socket + """ + lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(self.socket_path)) + if os.path.exists(self.socket_path): + try: + if self.sock: + self.sock.close() + if self.connection: + self.connection.close() + if self.connection.get_option("persistent_log_messages"): + for _level, message in self.connection.pop_messages(): + display.display(message, log_only=True) + except Exception: + pass + finally: + if os.path.exists(self.socket_path): + os.remove(self.socket_path) + setattr(self.connection, '_socket_path', None) + setattr(self.connection, '_connected', False) + + if os.path.exists(lock_path): + os.remove(lock_path) + + display.display('shutdown complete', log_only=True) + + +def main(): + """ Called to initiate the connect to the remote device + """ + rc = 0 + result = {} + messages = list() + socket_path = None + + # Need stdin as a byte stream + if PY3: + stdin = sys.stdin.buffer + else: + stdin = sys.stdin + + # Note: update the below log capture code after Display.display() is refactored. + saved_stdout = sys.stdout + sys.stdout = StringIO() + + try: + # read the play context data via stdin, which means depickling it + vars_data = read_stream(stdin) + init_data = read_stream(stdin) + + if PY3: + pc_data = cPickle.loads(init_data, encoding='bytes') + variables = cPickle.loads(vars_data, encoding='bytes') + else: + pc_data = cPickle.loads(init_data) + variables = cPickle.loads(vars_data) + + play_context = PlayContext() + play_context.deserialize(pc_data) + display.verbosity = play_context.verbosity + + except Exception as e: + rc = 1 + result.update({ + 'error': to_text(e), + 'exception': traceback.format_exc() + }) + + if rc == 0: + ssh = connection_loader.get('ssh', class_only=True) + ansible_playbook_pid = sys.argv[1] + task_uuid = sys.argv[2] + cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user, play_context.connection, ansible_playbook_pid) + # create the persistent connection dir if need be and create the paths + # which we will be using later + tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR) + makedirs_safe(tmp_path) + + socket_path = unfrackpath(cp % dict(directory=tmp_path)) + lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(socket_path)) + + with file_lock(lock_path): + if not os.path.exists(socket_path): + messages.append(('vvvv', 'local domain socket does not exist, starting it')) + original_path = os.getcwd() + r, w = os.pipe() + pid = fork_process() + + if pid == 0: + try: + os.close(r) + wfd = os.fdopen(w, 'w') + process = ConnectionProcess(wfd, play_context, socket_path, original_path, task_uuid, ansible_playbook_pid) + process.start(variables) + except Exception: + messages.append(('error', traceback.format_exc())) + rc = 1 + + if rc == 0: + process.run() + else: + process.shutdown() + + sys.exit(rc) + + else: + os.close(w) + rfd = os.fdopen(r, 'r') + data = json.loads(rfd.read(), cls=AnsibleJSONDecoder) + messages.extend(data.pop('messages')) + result.update(data) + + else: + messages.append(('vvvv', 'found existing local domain socket, using it!')) + conn = Connection(socket_path) + conn.set_options(var_options=variables) + pc_data = to_text(init_data) + try: + conn.update_play_context(pc_data) + conn.set_check_prompt(task_uuid) + except Exception as exc: + # Only network_cli has update_play context and set_check_prompt, so missing this is + # not fatal e.g. netconf + if isinstance(exc, ConnectionError) and getattr(exc, 'code', None) == -32601: + pass + else: + result.update({ + 'error': to_text(exc), + 'exception': traceback.format_exc() + }) + + if os.path.exists(socket_path): + messages.extend(Connection(socket_path).pop_messages()) + messages.append(('vvvv', sys.stdout.getvalue())) + result.update({ + 'messages': messages, + 'socket_path': socket_path + }) + + sys.stdout = saved_stdout + if 'exception' in result: + rc = 1 + sys.stderr.write(json.dumps(result, cls=AnsibleJSONEncoder)) + else: + rc = 0 + sys.stdout.write(json.dumps(result, cls=AnsibleJSONEncoder)) + + sys.exit(rc) + + +if __name__ == '__main__': + display = Display() + main() diff --git a/.local/bin/ansible-console b/.local/bin/ansible-console new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-console @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-doc b/.local/bin/ansible-doc new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-doc @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-galaxy b/.local/bin/ansible-galaxy new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-galaxy @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-inventory b/.local/bin/ansible-inventory new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-inventory @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-playbook b/.local/bin/ansible-playbook new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-playbook @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-pull b/.local/bin/ansible-pull new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-pull @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/ansible-test b/.local/bin/ansible-test new file mode 100755 index 0000000..b96a777 --- /dev/null +++ b/.local/bin/ansible-test @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# PYTHON_ARGCOMPLETE_OK +"""Command line entry point for ansible-test.""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys + + +def main(): + """Main program entry point.""" + ansible_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + source_root = os.path.join(ansible_root, 'test', 'lib') + + if os.path.exists(os.path.join(source_root, 'ansible_test', '_internal', 'cli.py')): + # running from source, use that version of ansible-test instead of any version that may already be installed + sys.path.insert(0, source_root) + + # noinspection PyProtectedMember + from ansible_test._internal.cli import main as cli_main + + cli_main() + + +if __name__ == '__main__': + main() diff --git a/.local/bin/ansible-vault b/.local/bin/ansible-vault new file mode 120000 index 0000000..cabb1f5 --- /dev/null +++ b/.local/bin/ansible-vault @@ -0,0 +1 @@ +ansible \ No newline at end of file diff --git a/.local/bin/backupSynology b/.local/bin/backupSynology new file mode 100755 index 0000000..7014b6c --- /dev/null +++ b/.local/bin/backupSynology @@ -0,0 +1,18 @@ +#!/bin/bash +source ~/.password +mkdir -p ~/Movies/{Anime,Videos} +mkdir -p ~/Music +mkdir -p ~/Collage + +#sudo mount -t cifs //192.168.0.220/Mega -o username=${USERNAME},password=${PASSWORD},vers=2\.0 /mnt/Synology +#sudo mount -t cifs //192.168.0.220/music -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Music +#sudo mount -t cifs //192.168.0.220/usbshare1/Video -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Movies/Videos +#sudo mount -t cifs //192.168.0.220/usbshare1/Anime -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Movies/Anime +#sudo mount -t cifs //192.168.0.220/Studia -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Collage + +if [ -e /mnt/Synology/Systems ] +then + echo "Starting $(date)" > ~/.cache/.logSynology + sudo rsync -r --bwlimit=1024 --update --progress /usr/mega/ /mnt/Synology >> ~/.cache/.logSynology + echo "Ending $(date)" >> ~/.cache/.logSynology +fi diff --git a/.local/bin/bin-cp b/.local/bin/bin-cp new file mode 100755 index 0000000..725956c --- /dev/null +++ b/.local/bin/bin-cp @@ -0,0 +1,8 @@ +#!/bin/bash +chmod +x * +chown -R yorune: * +sudo cp -pr * /bin/ +#sudo cp /etc/bash.bashrc ../configs +#cp /home/yorune/Arch/configs/zshrc /home/yorune/.zshrc +#cp /home/yorune/Arch/configs/vimrc /home/yorune/.vimrc +echo DONE!! diff --git a/.local/bin/browser-x b/.local/bin/browser-x new file mode 100755 index 0000000..46a08b9 --- /dev/null +++ b/.local/bin/browser-x @@ -0,0 +1,12 @@ +#!/bin/bash +export GTK_IM_MODULE=ibus +export XMODIFIERS=@im=ibus +export QT_IM_MODULE=ibus + +KERNEL=$(uname -sr) +[[ $KERNEL =~ "icrosoft" ]] && "/mnt/c/Program Files/Mozilla Firefox/firefox.exe" "$@" +[[ $KERNEL =~ "gentoo" ]] && GDK_DPI_SCALE="1.2" firefox-bin "$@" +#[[ $KERNEL =~ "gentoo" ]] && __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 GDK_DPI_SCALE="1.2" firefox "$@" + +# Firefox +# media.ffmpeg.vaapi.enabled -> true diff --git a/.local/bin/chardetect b/.local/bin/chardetect new file mode 100755 index 0000000..b854f52 --- /dev/null +++ b/.local/bin/chardetect @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from chardet.cli.chardetect import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/cleaner b/.local/bin/cleaner new file mode 100755 index 0000000..755f014 --- /dev/null +++ b/.local/bin/cleaner @@ -0,0 +1,15 @@ +#!/bin/bash +set -x +sudo rm -rf /var/tmp/portage/* +sudo rm -rf /var/tmp/binpkgs/* +sudo rm -rf /var/tmp/genkernel/* +sudo rm -rf /var/cache/genkernel/* +sudo emerge -a --depclean +sudo emerge -a @preserved-rebuild +sudo eclean -C -q packages +sudo eclean -C -q -d -t1w distfiles +sudo revdep-rebuild +sudo perl-cleaner --all +sudo etc-update +sudo env-update +source /etc/profile diff --git a/.local/bin/cleanertmp b/.local/bin/cleanertmp new file mode 100755 index 0000000..f7d56c6 --- /dev/null +++ b/.local/bin/cleanertmp @@ -0,0 +1,6 @@ +sudo rm -rf /var/tmp/portage/* +sudo rm -rf /var/tmp/binpkgs/* +sudo rm -rf /var/tmp/genkernel/* +sudo rm -rf /var/cache/genkernel/* +sudo eclean-dist -d +sudo eclean-pkg -d diff --git a/.local/bin/cmus-control b/.local/bin/cmus-control new file mode 100755 index 0000000..795d0d2 --- /dev/null +++ b/.local/bin/cmus-control @@ -0,0 +1,5 @@ +#!/bin/bash +pkill -RTMIN+11 dwmblocks +[ "$1" = "play" ] && cmus-remote -u +[ "$1" = "next" ] && cmus-remote -n +[ "$1" = "prev" ] && cmus-remote -r diff --git a/.local/bin/cmus-shell b/.local/bin/cmus-shell new file mode 100755 index 0000000..35a46ef --- /dev/null +++ b/.local/bin/cmus-shell @@ -0,0 +1,4 @@ +#!/bin/bash +if ! screen -r -D cmus >/dev/null ; then + screen -S cmus /usr/bin/cmus "$@" +fi diff --git a/.local/bin/dmenumount b/.local/bin/dmenumount new file mode 100755 index 0000000..dcdf08f --- /dev/null +++ b/.local/bin/dmenumount @@ -0,0 +1,59 @@ +#!/bin/sh +# Gives a dmenu prompt to mount unmounted drives. +# If they're in /etc/fstab, they'll be mounted automatically. +# Otherwise, you'll be prompted to give a mountpoint from already existsing directories. +# If you input a novel directory, it will prompt you to create that directory. + +getmount() { \ + [ -z "$chosen" ] && exit 1 + mp="$(find $1 | dmenu -i -p "Type in mount point.")" + [ "$mp" = "" ] && exit 1 + if [ ! -d "$mp" ]; then + mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?") + [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp") + fi + } + +mountusb() { \ + chosen="$(echo "$usbdrives" | dmenu -i -p "Mount which drive?" | awk '{print $1}')" + sudo -A mount "$chosen" && notify-send "💻 USB mounting" "$chosen mounted." && exit 0 + alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$2=="part"&&$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not \( -path *%s -prune \) \ \n",$3}') + getmount "/mnt /media -maxdepth 5 -type d $alreadymounted" + partitiontype="$(lsblk -no "fstype" "$chosen")" + case "$partitiontype" in + "vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;; + *) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" 741 "$mp";; + esac + notify-send "💻 USB mounting" "$chosen mounted to $mp." + } + +mountandroid() { \ + chosen=$(echo "$anddrives" | dmenu -i -p "Which Android device?" | cut -d : -f 1) + getmount "/media -maxdepth 3 -type d" + sudo simple-mtpfs --device "$chosen" "$mp" + notify-send "🤖 Android Mounting" "Android device mounted to $mp." + } + +asktype() { \ + case $(printf "USB\\nAndroid" | dmenu -i -p "Mount a USB drive or Android device?") in + USB) mountusb ;; + Android) mountandroid ;; + esac + } + +anddrives=$(simple-mtpfs -l 2>/dev/null) +usbdrives="$(lsblk -rpo "name,type,size,mountpoint" | awk '$2=="part"&&$4==""{printf "%s (%s)\n",$1,$3}')" + +if [ -z "$usbdrives" ]; then + [ -z "$anddrives" ] && echo "No USB drive or Android device detected" && exit + echo "Android device(s) detected." + mountandroid +else + if [ -z "$anddrives" ]; then + echo "USB drive(s) detected." + mountusb + else + echo "Mountable USB drive(s) and Android device(s) detected." + asktype + fi +fi diff --git a/.local/bin/dmenuumount b/.local/bin/dmenuumount new file mode 100755 index 0000000..dee53e7 --- /dev/null +++ b/.local/bin/dmenuumount @@ -0,0 +1,41 @@ +#!/bin/sh +# A dmenu prompt to unmount drives. +# Provides you with mounted partitions, select one to unmount. +# Drives mounted at /, /boot and /home will not be options to unmount. + +unmountusb() { + [ -z "$drives" ] && exit + chosen=$(echo "$drives" | dmenu -i -p "Unmount which drive?" | awk '{print $1}') + [ -z "$chosen" ] && exit + sudo -A umount "$chosen" && notify-send "💻 USB unmounting" "$chosen unmounted." + } + +unmountandroid() { \ + chosen=$(awk '/simple-mtpfs/ {print $2}' /etc/mtab | dmenu -i -p "Unmount which device?") + [ -z "$chosen" ] && exit + sudo -A umount -l "$chosen" && notify-send "🤖 Android unmounting" "$chosen unmounted." + } + +asktype() { \ + case "$(printf "USB\\nAndroid" | dmenu -i -p "Unmount a USB drive or Android device?")" in + USB) unmountusb ;; + Android) unmountandroid ;; + esac + } + +drives=$(lsblk -nrpo "name,type,size,mountpoint" | awk '$2=="part"&&$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "%s (%s)\n",$4,$3}') + +if ! grep simple-mtpfs /etc/mtab; then + [ -z "$drives" ] && echo "No drives to unmount." && exit + echo "Unmountable USB drive detected." + unmountusb +else + if [ -z "$drives" ] + then + echo "Unmountable Android device detected." + unmountandroid + else + echo "Unmountable USB drive(s) and Android device(s) detected." + asktype + fi +fi diff --git a/.local/bin/dmenuunicode b/.local/bin/dmenuunicode new file mode 100755 index 0000000..6b1a175 --- /dev/null +++ b/.local/bin/dmenuunicode @@ -0,0 +1,18 @@ +#!/bin/sh +# Give dmenu list of all unicode characters to copy. +# Shows the selected character in dunst if running. + +# Must have xclip installed to even show menu. +xclip -h >/dev/null || exit + +chosen=$(grep -v "#" ~/dwm/emoji | dmenu -i -l 20 -fn Monospace-18) + +[ "$chosen" != "" ] || exit + +c=$(echo "$chosen" | sed "s/ .*//") +echo "$c" | tr -d '\n' | xclip -selection clipboard +notify-send "'$c' copied to clipboard." & + +s=$(echo "$chosen" | sed "s/.*; //" | awk '{print $1}') +echo "$s" | tr -d '\n' | xclip +notify-send "'$s' copied to primary." & diff --git a/.local/bin/docker-start b/.local/bin/docker-start new file mode 100755 index 0000000..cf3efe5 --- /dev/null +++ b/.local/bin/docker-start @@ -0,0 +1,17 @@ +#!/bin/bash - +#=============================================================================== +# +# FILE: docker-start.sh +# +# USAGE: ./docker-start.sh +# +# OPTIONS: --- +# REQUIREMENTS: openrc system +# AUTHOR: Marcin Woźniak, y0rune@aol.com +# CREATED: 11/08/2020 11:21 +# REVISION: --- +#=============================================================================== + +sudo rc-service docker start +sleep 10 +sudo docker ps diff --git a/.local/bin/dwmstatusbar b/.local/bin/dwmstatusbar new file mode 100755 index 0000000..2ce8159 --- /dev/null +++ b/.local/bin/dwmstatusbar @@ -0,0 +1,99 @@ +#!/bin/bash +print_weather() { +[ "$(stat -c %y "/home/yorune/.config/weatherreport" 2>/dev/null | cut -d' ' -f1)" != "$(date '+%Y-%m-%d')" ] && curl -s "wttr.in/$location" > "/home/yorune/.config/weatherreport" + +printf "%s" "$(sed '16q;d' "/home/yorune/.config/weatherreport" | grep -wo "[0-9]*%" | sort -n | sed -e '$!d' | sed -e "s/^/☔ /g" | tr -d '\n')" && sed '13q;d' "/home/yorune/.config/weatherreport" | grep -o "m\\(-\\)*[0-9]\\+" | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print" ❄",$1"°","☀",$2"°"}' +} + +print_volume() { +[ "$(pulsemixer --get-mute)" = "1" ] && printf "🔇" && exit +vol=$(pulsemixer --get-volume | awk '{print $1}') +printf "%s%%\\n" "🔊 $vol" +} + +print_wifi(){ + echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/")" + #echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/") $( [[ $(ip -br a show | awk {'print $1'}) =~ "vpn" ]] && echo "📡" || echo "❌" )" +} + +print_temp(){ + echo -e "🔥 $(sensors | awk '/Core 0/ {print int($3)"°C"}') $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')°C" +} + +print_date(){ + echo -e "🕛 $(date +"%d/%m %H:%M")" +} + +print_mem(){ + free --mebi | sed -n '2{p;q}' | awk '{printf ("🧠 %2.2fGiB", ( $3 / 1024) )}' +} + +print_music(){ +# Source: https://github.com/joestandring/dwm-bar +# A dwm_bar function that shows the current artist, track, position, duration, and status from cmus +# Joe Standring +# GNU GPLv3 +# Dependencies: cmus + +if ps -C cmus > /dev/null; then + ARTIST=$(cmus-remote -Q | grep -a '^tag artist' | awk '{gsub("tag artist ", "");print}') + TRACK=$(cmus-remote -Q | grep -a '^tag title' | awk '{gsub("tag title ", "");print}') + POSITION=$(cmus-remote -Q | grep -a '^position' | awk '{gsub("position ", "");print}') + DURATION=$(cmus-remote -Q | grep -a '^duration' | awk '{gsub("duration ", "");print}') + STATUS=$(cmus-remote -Q | grep -a '^status' | awk '{gsub("status ", "");print}') + SHUFFLE=$(cmus-remote -Q | grep -a '^set shuffle' | awk '{gsub("set shuffle ", "");print}') + + if [ "$STATUS" = "playing" ]; then + STATUS="▶" + else + STATUS="⏸" + fi + + #printf "%s%s %s - %s " "$STATUS" "$ARTIST" "$TRACK" + printf "%s" "$STATUS" + #printf "%0d:%02d/" $((POSITION%3600/60)) $((POSITION%60)) + #printf "%0d:%02d" $((DURATION%3600/60)) $((DURATION%60)) + #printf "%s\n" +fi +} + +print_battery() { + # Find the battery level + hash acpi || return 0 + onl="$(acpi -V | grep "on-line")" + charge="$(cat /sys/class/power_supply/BAT*/capacity)" + time="$(awk '{print $5}' <(acpi))" + + # Determine battery glyph by percentage range + if [[ -z $onl && ${charge} -gt 80 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 80 && ${charge} -gt 60 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 60 && ${charge} -gt 40 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 40 && ${charge} -gt 20 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 20 ]]; then + echo -e "❗🔋 ${charge}% ${time}" + # If charging, use animated glyph + else + echo -e "🔌" + fi +} + +print_mail(){ + unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)" + icon="$(cat "/tmp/imapsyncicon_$USER" 2>/dev/null)" + [ "$unread" = "1" ] && [ "$icon" = "" ] || echo "📬 $unread$icon" +} + +print_cpu() { + cpuUse=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print int(100 - $1)"%"}') + echo -e "💻 $cpuUse" +} + +while true +do + xsetroot -name "$(print_music) $(print_temp) $(print_weather) $(print_mail) $(print_cpu) $(print_mem) $(print_wifi) $(print_battery) $(print_volume) $(print_date)" + sleep 10 +done diff --git a/.local/bin/eix-repos-sync b/.local/bin/eix-repos-sync new file mode 100755 index 0000000..185d16b --- /dev/null +++ b/.local/bin/eix-repos-sync @@ -0,0 +1,2 @@ +#!/bin/bash +for i in /usr/repos/*/.git; do ( echo $i; cd $i/..; sudo git pull; ); done diff --git a/.local/bin/emerge-logs b/.local/bin/emerge-logs new file mode 100755 index 0000000..0fac1ac --- /dev/null +++ b/.local/bin/emerge-logs @@ -0,0 +1,13 @@ +#!/bin/bash +LOGS=( $(sudo find /var/tmp/portage/ -mindepth 1 -maxdepth 5 -name "build.log") ) +LEN=${#LOGS[@]} + +for (( i=0; i<$LEN; i++ )); +do + echo "$(( $i + 1 )) - ${LOGS[$i]}" +done + +echo +read -p 'Select number to show logs: ' NUMBER + +sudo tail -f ${LOGS[$NUMBER-1]} diff --git a/.local/bin/epylint b/.local/bin/epylint new file mode 100755 index 0000000..93a57fa --- /dev/null +++ b/.local/bin/epylint @@ -0,0 +1,8 @@ +#!/usr/lib/python-exec/python3.7/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_epylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_epylint()) diff --git a/.local/bin/euscan b/.local/bin/euscan new file mode 100755 index 0000000..1fe7f3b --- /dev/null +++ b/.local/bin/euscan @@ -0,0 +1,4 @@ +#!/usr/lib/python-exec/python3.8/python +# EASY-INSTALL-SCRIPT: 'euscan==9999','euscan' +__requires__ = 'euscan==9999' +__import__('pkg_resources').run_script('euscan==9999', 'euscan') diff --git a/.local/bin/ext b/.local/bin/ext new file mode 100755 index 0000000..537cd2f --- /dev/null +++ b/.local/bin/ext @@ -0,0 +1,44 @@ +#!/bin/sh + +# A general, all-purpose extraction script. Not all extraction programs here +# are installed by LARBS automatically. +# +# Default behavior: Extract archive into new directory +# Behavior with `-c` option: Extract contents into current directory + +while getopts "hc" o; do case "${o}" in + c) extracthere="True" ;; + *) printf "Options:\\n -c: Extract archive into current directory rather than a new one.\\n" && exit 1 ;; +esac done + +if [ -z "$extracthere" ]; then + archive="$(readlink -f "$*")" && + directory="$(echo "$archive" | sed 's/\.[^\/.]*$//')" && + mkdir -p "$directory" && + cd "$directory" || exit 1 +else + archive="$(readlink -f "$(echo "$*" | cut -d' ' -f2)" 2>/dev/null)" +fi + +[ -z "$archive" ] && printf "Give archive to extract as argument.\\n" && exit 1 + +if [ -f "$archive" ] ; then + case "$archive" in + *.tar.bz2|*.tbz2) tar xvjf "$archive" ;; + *.tar.xz) tar -vxf "$archive" ;; + *.tar.gz|*.tgz) tar xvzf "$archive" ;; + *.lzma) unlzma "$archive" ;; + *.bz2) bunzip2 "$archive" ;; + *.rar) unrar x -ad "$archive" ;; + *.gz) gunzip "$archive" ;; + *.tar) tar xvf "$archive" ;; + *.zip) unzip "$archive" ;; + *.Z) uncompress "$archive" ;; + *.7z) 7z x "$archive" ;; + *.xz) unxz "$archive" ;; + *.exe) cabextract "$archive" ;; + *) printf "extract: '%s' - unknown archive method\\n" "$archive" ;; + esac +else + printf "File \"%s\" not found.\\n" "$archive" +fi diff --git a/.local/bin/f2py b/.local/bin/f2py new file mode 100755 index 0000000..7d02144 --- /dev/null +++ b/.local/bin/f2py @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/f2py3 b/.local/bin/f2py3 new file mode 100755 index 0000000..7d02144 --- /dev/null +++ b/.local/bin/f2py3 @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/f2py3.6 b/.local/bin/f2py3.6 new file mode 100755 index 0000000..bafb207 --- /dev/null +++ b/.local/bin/f2py3.6 @@ -0,0 +1,8 @@ +#!/usr/bin/python3.6 +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/f2py3.7 b/.local/bin/f2py3.7 new file mode 100755 index 0000000..b06de45 --- /dev/null +++ b/.local/bin/f2py3.7 @@ -0,0 +1,8 @@ +#!/usr/bin/python3.7 +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/f2py3.8 b/.local/bin/f2py3.8 new file mode 100755 index 0000000..7d02144 --- /dev/null +++ b/.local/bin/f2py3.8 @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from numpy.f2py.f2py2e import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/flask b/.local/bin/flask new file mode 100755 index 0000000..128df4e --- /dev/null +++ b/.local/bin/flask @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from flask.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/futurize b/.local/bin/futurize new file mode 100755 index 0000000..2d9c672 --- /dev/null +++ b/.local/bin/futurize @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from libfuturize.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/gentoo-test b/.local/bin/gentoo-test new file mode 100755 index 0000000..785d64d --- /dev/null +++ b/.local/bin/gentoo-test @@ -0,0 +1,28 @@ +#!/bin/bash + +sudo mount /dev/disk/by-uuid/c61f258f-5fd1-47f2-a270-bb8aa63b0020 /mnt/gentoo + +sleep 5 +sync + +if [ -e /dev/disk/by-uuid/c61f258f-5fd1-47f2-a270-bb8aa63b0020 ] +then + sudo mkdir -p /mnt/gentoo/proc + sudo mkdir -p /mnt/gentoo/dev + sudo mkdir -p /mnt/gentoo/sys + sudo mkdir -p /mnt/gentoo/mnt/backup/ccache + sudo cp -r $CCACHE_DIR/ccache.conf /mnt/gentoo/mnt/backup/ccache + sudo rsync -az -H /etc/portage/ /mnt/gentoo/gentoo/etc/portage/ + #sudo rsync -az -H /usr/portage/ /mnt/gentoo/gentoo/usr/portage/ + #sudo rsync -az -H /usr/repos/ /mnt/gentoo/gentoo/usr/repos/ + sudo mount --types proc /proc /mnt/gentoo/gentoo/proc + sudo mount --rbind /sys /mnt/gentoo/gentoo/sys + sudo mount --make-rslave /mnt/gentoo/gentoo/sys + sudo mount --rbind /dev /mnt/gentoo/gentoo/dev + sudo mount --make-rslave /mnt/gentoo/gentoo/dev + sleep 5 + sudo chroot /mnt/gentoo/gentoo /bin/bash + # echo "Europe/Warsaw" > /etc/timezone + # emerge --config sys-libs/timezone-data + # emerge dev-util/ccache app-shells/oh-my-zsh app-admin/sudo app-editors/vim +fi diff --git a/.local/bin/geoip b/.local/bin/geoip new file mode 100755 index 0000000..885fb84 --- /dev/null +++ b/.local/bin/geoip @@ -0,0 +1,3 @@ +#!/bin/bash + +curl -s ipinfo.io/$1 diff --git a/.local/bin/getforecast b/.local/bin/getforecast new file mode 100755 index 0000000..03e531b --- /dev/null +++ b/.local/bin/getforecast @@ -0,0 +1,16 @@ +#!/bin/sh +# Updates weather forecast + +FILE="$HOME/.config/.weatherreport.tmp" +FILER="$HOME/.config/weatherreport" +location="Kleczew" + +curl -s --max-time 10 "wttr.in/$location" > $FILE + +if [ -s "$FILE" ] +then + mv $FILE $FILER +# notify-send " Weather" "The weather forecast has been updated." +else + rm $FILE +fi diff --git a/.local/bin/gfetch b/.local/bin/gfetch new file mode 100755 index 0000000..ed7039c --- /dev/null +++ b/.local/bin/gfetch @@ -0,0 +1,133 @@ +#!/bin/sh + + +# Copyright (c) 2020, XGQT +# Licensed under the ISC License + +# .vir. +# ,d$$$$$$b. +# &&&&( )&&&b +# Q$$$$$$$$$$B +# "$$$$$$$P +# ,d$$$$$$P" +# $$$$$$P +# `Q$$P" + +# gfetch - tiny system info for gentoo + +# based on: +# https://github.com/jschx/ufetch/ + + +# INFO + +host="$(hostname 2>/dev/null)" +cpu="$(uname -p 2>/dev/null)" +kernel="$(uname -sr 2>/dev/null)" +uptime="$(uptime -p 2>/dev/null | sed 's/up //')" +shell="$(basename "${SHELL}" 2>/dev/null)" + +if [ -f /etc/lsb-release ] +then + os="$(cut -d \" -f 2 < /etc/lsb-release) $(uname -m)" +elif [ -f /etc/os-release ] +then + os="$(cut -d = -f 2 < /etc/os-release | sed 1q) $(uname -m)" +else + os="Gentoo $(uname -m)" +fi + +if [ -d "${EPREFIX}"/var/db/pkg ] +then + packages="All: $(find "${EPREFIX}"/var/db/pkg/*/* -type d | wc -l)" + real="Real: $(find "${EPREFIX}"/var/db/pkg/*/* -type d | grep -c -v -E 'acct-group|acct-user|app-eselect|java-virtuals|media-fonts|virtual')" +else + packages="n/a" + real="" +fi + +if [ -f "${EPREFIX}"/var/lib/portage/world ] +then + world="World: $(wc -l < "${EPREFIX}"/var/lib/portage/world)" +else + world="" +fi + +if [ -n "${DE}" ] +then + ui="${DE}" + uitype='DE' +elif [ -n "${WM}" ] +then + ui="${WM}" + uitype='WM' +elif [ -n "${XDG_CURRENT_DESKTOP}" ] +then + ui="${XDG_CURRENT_DESKTOP}" + uitype='DE' +elif [ -n "${DESKTOP_SESSION}" ] +then + ui="${DESKTOP_SESSION}" + uitype='DE' +elif [ -f "${HOME}/.xinitrc" ] +then + ui="$(tail -n 1 "${HOME}/.xinitrc" | cut -d ' ' -f 2)" + uitype='WM' +elif [ -f "${HOME}/.xsession" ] +then + ui="$(tail -n 1 "${HOME}/.xsession" | cut -d ' ' -f 2)" + uitype='WM' +else + ui='unknown' + uitype='UI' +fi + + +# Color Definitions + +if [ -x "$(command -v tput)" ]; then + bold="$(tput bold)" + # black="$(tput setaf 0)" + # red="$(tput setaf 1)" + # green="$(tput setaf 2)" + # yellow="$(tput setaf 3)" + # blue="$(tput setaf 4)" + magenta="$(tput setaf 5)" + # cyan="$(tput setaf 6)" + white="$(tput setaf 7)" + reset="$(tput sgr0)" +fi + + +# Output color + +# labels +lc="${reset}${bold}${magenta}" + +# user and hostname +nc="${reset}${bold}${magenta}" + +# info +ic="${reset}${bold}${white}" + +# first color +c0="${reset}${bold}${magenta}" + +# second color +c1="${reset}${magenta}" + + +# OUTPUT + +cat < beggining.txt +sed -n '/# END CONFIGURATION:/,/# END OF CONFIGURATION/p' $FINAL > end.txt + +for blockedCountries in "${BLOCKEDCOUNTRIES[@]}" +do + mv allzones/$blockedCountries.zone blocked/ +done + +for f in blocked/*.zone; +do + echo "Adding $f to iptables.." + echo "" >> $TEMP + echo "# Adding zone $f" >> $TEMP + + while read line; do + echo "iptables -A INPUT -s $line -j DROP" >> $TEMP + #echo "-A INPUT -s $line -j DROP" >> $TEMP + done < $f +done + +cat beggining.txt > $FINAL +cat $TEMP >> $FINAL +echo "" >> $FINAL +cat end.txt >> $FINAL + +rm -rf blocked allzones $TEMP beggining.txt end.txt +sh ~/.local/bin/iptables-restart +#systemctl restart iptables diff --git a/.local/bin/isort b/.local/bin/isort new file mode 100755 index 0000000..348d6c4 --- /dev/null +++ b/.local/bin/isort @@ -0,0 +1,8 @@ +#!/usr/lib/python-exec/python3.7/python +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/mailsync b/.local/bin/mailsync new file mode 100755 index 0000000..6f06c05 --- /dev/null +++ b/.local/bin/mailsync @@ -0,0 +1,73 @@ +#!/bin/bash + +# Sync mail and give notification if there is new mail. +# Run only if user logged in (prevent cron errors) +export DISPLAY=:0 + +pgrep -u "${USER:=$LOGNAME}" >/dev/null || { echo "$USER not logged in; sync will not run."; exit ;} +# Run only if not already running in other instance +pgrep -x mbsync >/dev/null && { echo "mbsync is already running." ; exit ;} + +# Checks for internet connection and set notification script. +ping -q -c 1 1.1.1.1 > /dev/null || { echo "No internet connection detected."; exit ;} +command -v notify-send >/dev/null || echo "Note that \`libnotify\` or \`libnotify-send\` should be installed for pop-up mail notifications with this script." + +# Required to display notifications if run as a cronjob: +DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus + +# For individual configurations: +[ -d "$HOME/.local/share/password-store" ] && export PASSWORD_STORE_DIR="$HOME/.local/share/password-store" + +# Settings are different for MacOS (Darwin) systems. +if [ "$(uname)" = "Darwin" ]; then + notify() { osascript -e "display notification \"$2 in $1\" with title \"You've got Mail\" subtitle \"Account: $account\"" && sleep 2 ;} + messageinfo() { osascript "display notification with title \"📧 $from\" subtitle \"$subject\"" ;} +else + notify() { notify-send --app-name="mutt-wizard" "mutt-wizard" "📬 $2 new mail(s) in \`$1\` account." ;} + messageinfo() { notify-send --app-name="mutt-wizard" "📧$from:" "$subject" ;} +fi + +# Check account for new mail. Notify if there is new content. +syncandnotify() { + acc="$(echo "$account" | sed "s/.*\///")" + mbsync $opts "$acc" + new=$(find $(ls $HOME/.local/share/mail/$acc/* | grep -i inbox | sed 's/\://')/new -type f -newer "$HOME/.config/mutt/.mailsynclastrun" 2> /dev/null) + newcount=$(echo "$new" | sed '/^\s*$/d' | wc -l) + if [ "$newcount" -gt "0" ]; then + notify "$acc" "$newcount" & + for file in $new; do + # Extract subject and sender from mail. + from=$(awk '/^From: / && ++n ==1,/^\<.*\>:/' "$file" | perl -CS -MEncode -ne 'print decode("MIME-Header", $_)' | awk '{ $1=""; if (NF>=3)$NF=""; print $0 }' | sed 's/^[[:blank:]]*[\"'\''\<]*//;s/[\"'\''\>]*[[:blank:]]*$//') + subject=$(awk '/^Subject: / && ++n == 1,/^\<.*\>: / && ++i == 2' "$file" | head -n-1 | perl -CS -MEncode -ne 'print decode("MIME-Header", $_)' | sed 's/^Subject: //' | sed 's/^{[[:blank:]]*[\"'\''\<]*//;s/[\"'\''\>]*[[:blank:]]*$//' | tr -d '\n') + messageinfo & + done + fi +} + +# Sync accounts passed as argument or all. +if [ "$#" -eq "0" ]; then + accounts="$(awk '/^Channel/ {print $2}' "$HOME/.mbsyncrc")" +else + for arg in "$@"; do + [ "${arg%${arg#?}}" = '-' ] && opts="${opts:+${opts} }${arg}" && shift 1 + done + accounts=$* +fi + +echo " 🔃" > /tmp/imapsyncicon_"$USER" +( pkill -RTMIN+12 "${STATUSBAR:-blocks}" >/dev/null 2>&1 ) 2>/dev/null + +# Parallelize multiple accounts +for account in $accounts +do + syncandnotify & +done + +wait +rm -f /tmp/imapsyncicon_"$USER" +( pkill -RTMIN+12 "${STATUSBAR:-blocks}" >/dev/null 2>&1 ) 2>/dev/null + +notmuch new 2>/dev/null + +#Create a touch file that indicates the time of the last run of mailsync +touch "$HOME/.config/mutt/.mailsynclastrun" diff --git a/.local/bin/minecraft-launcher b/.local/bin/minecraft-launcher new file mode 100755 index 0000000..6829f8f --- /dev/null +++ b/.local/bin/minecraft-launcher @@ -0,0 +1,2 @@ +#!/bin/bash +__NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 java -jar ~/Downloads/TLauncher-2.75/TLauncher-2.75.jar diff --git a/.local/bin/mouse-set b/.local/bin/mouse-set new file mode 100755 index 0000000..8940eba --- /dev/null +++ b/.local/bin/mouse-set @@ -0,0 +1,10 @@ +#!/bin/bash +choices="Off\nCycle" +chosen=$(echo -e "$choices" | dmenu -i) + +mouse=$(sudo ratbag-command list | awk '{print $1}' | sed 's/://') + +case "$chosen" in + Off) sudo ratbag-command led 0 set mode off $mouse ;; + Cycle) sudo ratbag-command led 0 set mode cycle $mouse && sudo ratbag-command led 0 set rate 25000 $mouse ;; +esac diff --git a/.local/bin/night b/.local/bin/night new file mode 100755 index 0000000..8814e36 --- /dev/null +++ b/.local/bin/night @@ -0,0 +1,2 @@ +#!/bin/bash +redshift -l 52.2327:18.3036 -t 6500:3200& diff --git a/.local/bin/notify-program b/.local/bin/notify-program new file mode 100755 index 0000000..5621842 --- /dev/null +++ b/.local/bin/notify-program @@ -0,0 +1,4 @@ +#!/bin/bash +KERNEL=$(uname -sr) +[[ $KERNEL =~ "icrosoft" ]] && $HOME/.local/bin/wsl-notify "$@" +[[ $KERNEL =~ "gentoo" ]] && /usr/bin/notify-send "$@" diff --git a/.local/bin/password-manager b/.local/bin/password-manager new file mode 100755 index 0000000..5484b7a --- /dev/null +++ b/.local/bin/password-manager @@ -0,0 +1,2 @@ +#!/bin/bash +QT_SCALE_FACTOR=1.5 keepassxc diff --git a/.local/bin/pasteurize b/.local/bin/pasteurize new file mode 100755 index 0000000..fc74807 --- /dev/null +++ b/.local/bin/pasteurize @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from libpasteurize.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/pwiz.py b/.local/bin/pwiz.py new file mode 100755 index 0000000..5cdebdb --- /dev/null +++ b/.local/bin/pwiz.py @@ -0,0 +1,223 @@ +#!/usr/bin/python3.8 + +import datetime +import sys +from getpass import getpass +from optparse import OptionParser + +from peewee import * +from peewee import print_ +from peewee import __version__ as peewee_version +from playhouse.cockroachdb import CockroachDatabase +from playhouse.reflection import * + + +HEADER = """from peewee import *%s + +database = %s('%s'%s) +""" + +BASE_MODEL = """\ +class BaseModel(Model): + class Meta: + database = database +""" + +UNKNOWN_FIELD = """\ +class UnknownField(object): + def __init__(self, *_, **__): pass +""" + +DATABASE_ALIASES = { + CockroachDatabase: ['cockroach', 'cockroachdb', 'crdb'], + MySQLDatabase: ['mysql', 'mysqldb'], + PostgresqlDatabase: ['postgres', 'postgresql'], + SqliteDatabase: ['sqlite', 'sqlite3'], +} + +DATABASE_MAP = dict((value, key) + for key in DATABASE_ALIASES + for value in DATABASE_ALIASES[key]) + +def make_introspector(database_type, database_name, **kwargs): + if database_type not in DATABASE_MAP: + err('Unrecognized database, must be one of: %s' % + ', '.join(DATABASE_MAP.keys())) + sys.exit(1) + + schema = kwargs.pop('schema', None) + DatabaseClass = DATABASE_MAP[database_type] + db = DatabaseClass(database_name, **kwargs) + return Introspector.from_database(db, schema=schema) + +def print_models(introspector, tables=None, preserve_order=False, + include_views=False, ignore_unknown=False, snake_case=True): + database = introspector.introspect(table_names=tables, + include_views=include_views, + snake_case=snake_case) + + db_kwargs = introspector.get_database_kwargs() + header = HEADER % ( + introspector.get_additional_imports(), + introspector.get_database_class().__name__, + introspector.get_database_name(), + ', **%s' % repr(db_kwargs) if db_kwargs else '') + print_(header) + + if not ignore_unknown: + print_(UNKNOWN_FIELD) + + print_(BASE_MODEL) + + def _print_table(table, seen, accum=None): + accum = accum or [] + foreign_keys = database.foreign_keys[table] + for foreign_key in foreign_keys: + dest = foreign_key.dest_table + + # In the event the destination table has already been pushed + # for printing, then we have a reference cycle. + if dest in accum and table not in accum: + print_('# Possible reference cycle: %s' % dest) + + # If this is not a self-referential foreign key, and we have + # not already processed the destination table, do so now. + if dest not in seen and dest not in accum: + seen.add(dest) + if dest != table: + _print_table(dest, seen, accum + [table]) + + print_('class %s(BaseModel):' % database.model_names[table]) + columns = database.columns[table].items() + if not preserve_order: + columns = sorted(columns) + primary_keys = database.primary_keys[table] + for name, column in columns: + skip = all([ + name in primary_keys, + name == 'id', + len(primary_keys) == 1, + column.field_class in introspector.pk_classes]) + if skip: + continue + if column.primary_key and len(primary_keys) > 1: + # If we have a CompositeKey, then we do not want to explicitly + # mark the columns as being primary keys. + column.primary_key = False + + is_unknown = column.field_class is UnknownField + if is_unknown and ignore_unknown: + disp = '%s - %s' % (column.name, column.raw_column_type or '?') + print_(' # %s' % disp) + else: + print_(' %s' % column.get_field()) + + print_('') + print_(' class Meta:') + print_(' table_name = \'%s\'' % table) + multi_column_indexes = database.multi_column_indexes(table) + if multi_column_indexes: + print_(' indexes = (') + for fields, unique in sorted(multi_column_indexes): + print_(' ((%s), %s),' % ( + ', '.join("'%s'" % field for field in fields), + unique, + )) + print_(' )') + + if introspector.schema: + print_(' schema = \'%s\'' % introspector.schema) + if len(primary_keys) > 1: + pk_field_names = sorted([ + field.name for col, field in columns + if col in primary_keys]) + pk_list = ', '.join("'%s'" % pk for pk in pk_field_names) + print_(' primary_key = CompositeKey(%s)' % pk_list) + elif not primary_keys: + print_(' primary_key = False') + print_('') + + seen.add(table) + + seen = set() + for table in sorted(database.model_names.keys()): + if table not in seen: + if not tables or table in tables: + _print_table(table, seen) + +def print_header(cmd_line, introspector): + timestamp = datetime.datetime.now() + print_('# Code generated by:') + print_('# python -m pwiz %s' % cmd_line) + print_('# Date: %s' % timestamp.strftime('%B %d, %Y %I:%M%p')) + print_('# Database: %s' % introspector.get_database_name()) + print_('# Peewee version: %s' % peewee_version) + print_('') + + +def err(msg): + sys.stderr.write('\033[91m%s\033[0m\n' % msg) + sys.stderr.flush() + +def get_option_parser(): + parser = OptionParser(usage='usage: %prog [options] database_name') + ao = parser.add_option + ao('-H', '--host', dest='host') + ao('-p', '--port', dest='port', type='int') + ao('-u', '--user', dest='user') + ao('-P', '--password', dest='password', action='store_true') + engines = sorted(DATABASE_MAP) + ao('-e', '--engine', dest='engine', default='postgresql', choices=engines, + help=('Database type, e.g. sqlite, mysql, postgresql or cockroachdb. ' + 'Default is "postgresql".')) + ao('-s', '--schema', dest='schema') + ao('-t', '--tables', dest='tables', + help=('Only generate the specified tables. Multiple table names should ' + 'be separated by commas.')) + ao('-v', '--views', dest='views', action='store_true', + help='Generate model classes for VIEWs in addition to tables.') + ao('-i', '--info', dest='info', action='store_true', + help=('Add database information and other metadata to top of the ' + 'generated file.')) + ao('-o', '--preserve-order', action='store_true', dest='preserve_order', + help='Model definition column ordering matches source table.') + ao('-I', '--ignore-unknown', action='store_true', dest='ignore_unknown', + help='Ignore fields whose type cannot be determined.') + ao('-L', '--legacy-naming', action='store_true', dest='legacy_naming', + help='Use legacy table- and column-name generation.') + return parser + +def get_connect_kwargs(options): + ops = ('host', 'port', 'user', 'schema') + kwargs = dict((o, getattr(options, o)) for o in ops if getattr(options, o)) + if options.password: + kwargs['password'] = getpass() + return kwargs + + +if __name__ == '__main__': + raw_argv = sys.argv + + parser = get_option_parser() + options, args = parser.parse_args() + + if len(args) < 1: + err('Missing required parameter "database"') + parser.print_help() + sys.exit(1) + + connect = get_connect_kwargs(options) + database = args[-1] + + tables = None + if options.tables: + tables = [table.strip() for table in options.tables.split(',') + if table.strip()] + + introspector = make_introspector(options.engine, database, **connect) + if options.info: + cmd_line = ' '.join(raw_argv[1:]) + print_header(cmd_line, introspector) + + print_models(introspector, tables, options.preserve_order, options.views, + options.ignore_unknown, not options.legacy_naming) diff --git a/.local/bin/pylint b/.local/bin/pylint new file mode 100755 index 0000000..3ec7b9e --- /dev/null +++ b/.local/bin/pylint @@ -0,0 +1,8 @@ +#!/usr/lib/python-exec/python3.7/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pylint()) diff --git a/.local/bin/pyreverse b/.local/bin/pyreverse new file mode 100755 index 0000000..e498454 --- /dev/null +++ b/.local/bin/pyreverse @@ -0,0 +1,8 @@ +#!/usr/lib/python-exec/python3.7/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pyreverse +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pyreverse()) diff --git a/.local/bin/rasp b/.local/bin/rasp new file mode 100755 index 0000000..412a215 --- /dev/null +++ b/.local/bin/rasp @@ -0,0 +1,5 @@ +#!/bin/bash +sudo chown yorune: /mnt/rasp +sshfs root@192.168.0.222:/mnt/ /mnt/rasp/ +sleep 10 +cd /mnt/rasp diff --git a/.local/bin/rcon b/.local/bin/rcon new file mode 100755 index 0000000..7cb5dc5 Binary files /dev/null and b/.local/bin/rcon differ diff --git a/.local/bin/record b/.local/bin/record new file mode 100755 index 0000000..47080ee --- /dev/null +++ b/.local/bin/record @@ -0,0 +1,19 @@ +#!/bin/bash +AUDIO=$(pactl list sources | awk '/Name: alsa_out/{print $2}' | head -n1) + +pkill -9 xautolock; xset s 10800 + +ffmpeg \ +-f pulse \ +-i "$AUDIO" \ +-f x11grab \ +-framerate 60 \ +-r 30 \ +-s 1920x1080 \ +-i :0 \ +-b:v 5500k \ +-c:v h264 -preset ultrafast -c:a aac \ +"$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4" + +xset s 600& +xautolock -time 15 -locker slock& diff --git a/.local/bin/remove-kernel b/.local/bin/remove-kernel new file mode 100755 index 0000000..95bf885 --- /dev/null +++ b/.local/bin/remove-kernel @@ -0,0 +1,6 @@ +#!/bin/bash +sudo emerge -C "=sys-kernel/gentoo-sources-$1" +sudo rm -rf /usr/src/linux-$1-gentoo +sudo rm -rf /lib/modules/$1-gentoo-x86_64 +sudo rm -rf /boot/*$1* +sudo grub-mkconfig -o /boot/grub/grub.cfg diff --git a/.local/bin/runJava b/.local/bin/runJava new file mode 100755 index 0000000..a6fefc5 --- /dev/null +++ b/.local/bin/runJava @@ -0,0 +1,2 @@ +#!/bin/sh + __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 DRI_PRIME=1 exec java "$@" diff --git a/.local/bin/saver-off b/.local/bin/saver-off new file mode 100755 index 0000000..2e719df --- /dev/null +++ b/.local/bin/saver-off @@ -0,0 +1,4 @@ +#!/bin/bash +xset s off +xset -dpms +pkill -9 xautolock diff --git a/.local/bin/sb-battery b/.local/bin/sb-battery new file mode 100755 index 0000000..33e08a9 --- /dev/null +++ b/.local/bin/sb-battery @@ -0,0 +1,25 @@ +#!/bin/bash +print_battery() { + # Find the battery level + hash acpi || return 0 + onl="$(acpi -V | grep "on-line")" + charge="$(cat /sys/class/power_supply/BAT*/capacity)" + time="$(awk '{print $5}' <(acpi))" + + # Determine battery glyph by percentage range + if [[ -z $onl && ${charge} -gt 80 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 80 && ${charge} -gt 60 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 60 && ${charge} -gt 40 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 40 && ${charge} -gt 20 ]]; then + echo -e "🔋 ${charge}% ${time}" + elif [[ -z $onl && ${charge} -le 20 ]]; then + echo -e "❗🔋 ${charge}% ${time}" + # If charging, use animated glyph + else + echo -e "🔌" + fi +} +print_battery \ No newline at end of file diff --git a/.local/bin/sb-clock b/.local/bin/sb-clock new file mode 100755 index 0000000..52e77dc --- /dev/null +++ b/.local/bin/sb-clock @@ -0,0 +1,5 @@ +#!/bin/bash +print_date(){ + echo -e "🕛 $(date +"%d/%m %H:%M")" +} +print_date diff --git a/.local/bin/sb-cpu b/.local/bin/sb-cpu new file mode 100755 index 0000000..917f1ab --- /dev/null +++ b/.local/bin/sb-cpu @@ -0,0 +1,6 @@ +#!/bin/bash +print_cpu() { + cpuUse=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print int(100 - $1)"%"}') + echo -e "💻 $cpuUse" +} +print_cpu \ No newline at end of file diff --git a/.local/bin/sb-mail b/.local/bin/sb-mail new file mode 100755 index 0000000..0d9d744 --- /dev/null +++ b/.local/bin/sb-mail @@ -0,0 +1,7 @@ +#!/bin/bash +print_mail(){ + unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)" + icon="$(cat "/tmp/imapsyncicon_$USER" 2>/dev/null)" + [ "$unread" = "1" ] && [ "$icon" = "" ] || echo "📬 $unread$icon" +} +print_mail \ No newline at end of file diff --git a/.local/bin/sb-mem b/.local/bin/sb-mem new file mode 100755 index 0000000..9d435ed --- /dev/null +++ b/.local/bin/sb-mem @@ -0,0 +1,5 @@ +#!/bin/bash +print_mem(){ + free --mebi | sed -n '2{p;q}' | awk '{printf ("🧠 %2.2fGiB", ( $3 / 1024) )}' +} +print_mem \ No newline at end of file diff --git a/.local/bin/sb-music b/.local/bin/sb-music new file mode 100755 index 0000000..8cf5f82 --- /dev/null +++ b/.local/bin/sb-music @@ -0,0 +1,30 @@ +#!/bin/bash +print_music(){ +# Source: https://github.com/joestandring/dwm-bar +# A dwm_bar function that shows the current artist, track, position, duration, and status from cmus +# Joe Standring +# GNU GPLv3 +# Dependencies: cmus + +if ps -C cmus > /dev/null; then + ARTIST=$(cmus-remote -Q | grep -a '^tag artist' | awk '{gsub("tag artist ", "");print}') + TRACK=$(cmus-remote -Q | grep -a '^tag title' | awk '{gsub("tag title ", "");print}') + POSITION=$(cmus-remote -Q | grep -a '^position' | awk '{gsub("position ", "");print}') + DURATION=$(cmus-remote -Q | grep -a '^duration' | awk '{gsub("duration ", "");print}') + STATUS=$(cmus-remote -Q | grep -a '^status' | awk '{gsub("status ", "");print}') + SHUFFLE=$(cmus-remote -Q | grep -a '^set shuffle' | awk '{gsub("set shuffle ", "");print}') + + if [ "$STATUS" = "playing" ]; then + STATUS="▶" + else + STATUS="⏸" + fi + + #printf "%s%s %s - %s " "$STATUS" "$ARTIST" "$TRACK" + printf "%s" "$STATUS" + #printf "%0d:%02d/" $((POSITION%3600/60)) $((POSITION%60)) + #printf "%0d:%02d" $((DURATION%3600/60)) $((DURATION%60)) + #printf "%s\n" +fi +} +print_music \ No newline at end of file diff --git a/.local/bin/sb-network b/.local/bin/sb-network new file mode 100755 index 0000000..9dca64c --- /dev/null +++ b/.local/bin/sb-network @@ -0,0 +1,5 @@ +#!/bin/bash +print_wifi(){ + echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/")" +} +print_wifi \ No newline at end of file diff --git a/.local/bin/sb-temp b/.local/bin/sb-temp new file mode 100755 index 0000000..65e2498 --- /dev/null +++ b/.local/bin/sb-temp @@ -0,0 +1,5 @@ +#!/bin/bash +print_temp(){ + echo -e "🔥 $(sensors | awk '/Core 0/ {print int($3)"°C"}') $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')°C" +} +print_temp \ No newline at end of file diff --git a/.local/bin/sb-volume b/.local/bin/sb-volume new file mode 100755 index 0000000..c51bd2b --- /dev/null +++ b/.local/bin/sb-volume @@ -0,0 +1,7 @@ +#!/bin/bash +print_volume() { + [ "$(pulsemixer --get-mute)" = "1" ] && printf "🔇" && exit + vol=$(pulsemixer --get-volume | awk '{print $1}') + printf "%s%%\\n" "🔊 $vol" +} +print_volume diff --git a/.local/bin/sb-weather b/.local/bin/sb-weather new file mode 100755 index 0000000..a33804b --- /dev/null +++ b/.local/bin/sb-weather @@ -0,0 +1,7 @@ +#!/bin/bash +print_weather() { +[ "$(stat -c %y "/home/yorune/.config/weatherreport" 2>/dev/null | cut -d' ' -f1)" != "$(date '+%Y-%m-%d')" ] && curl -s "wttr.in/$location" > "/home/yorune/.config/weatherreport" + +printf "%s" "$(sed '16q;d' "/home/yorune/.config/weatherreport" | grep -wo "[0-9]*%" | sort -n | sed -e '$!d' | sed -e "s/^/☔ /g" | tr -d '\n')" && sed '13q;d' "/home/yorune/.config/weatherreport" | grep -o "m\\(-\\)*[0-9]\\+" | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print" ❄",$1"°","☀",$2"°"}' +} +print_weather diff --git a/.local/bin/screen-switcher b/.local/bin/screen-switcher new file mode 100755 index 0000000..d4b7596 --- /dev/null +++ b/.local/bin/screen-switcher @@ -0,0 +1,13 @@ +#!/bin/bash +choices="Left\nRight\nDuplicated\nHDMI\nMonitor\nTV\n" + +chosen=$(echo -e "$choices" | dmenu -i) + +case "$chosen" in + Monitor) mons -o ;; + Duplicated) mons -d ;; + Left) mons -e left ;; + Right) mons -e right ;; + HDMI) mons -s ;; + TV) xrandr --output HDMI-1 --left-of eDP-1 --mode 1366x768 --rate 60 +esac diff --git a/.local/bin/screenshot b/.local/bin/screenshot new file mode 100755 index 0000000..ca085ef --- /dev/null +++ b/.local/bin/screenshot @@ -0,0 +1,7 @@ +FILE="/tmp/`date +%Y%m%d-%H.%M.%S.png`" +NAME=$(date +%s | sha256sum | base64 | head -c 20 ; echo) + +gnome-screenshot -f "$FILE" +rsync -avz --delete $FILE -e "ssh -p 22" root@209.250.255.224:/var/www/yorune.pl/u/$NAME.png && + +echo -e "https://yorune.pl/u/$NAME.png" | xclip -selection clipboard && notify-send "Screenshot has been updated" diff --git a/.local/bin/screenshot-area b/.local/bin/screenshot-area new file mode 100755 index 0000000..50277a3 --- /dev/null +++ b/.local/bin/screenshot-area @@ -0,0 +1,7 @@ +FILE="/tmp/`date +%Y%m%d-%H.%M.%S.png`" +NAME=$(date +%s | sha256sum | base64 | head -c 20 ; echo) + +gnome-screenshot --area -f "$FILE" +rsync -avz --delete $FILE -e "ssh -p 22" root@209.250.255.224:/var/www/yorune.pl/u/$NAME.png && + +echo -e "https://yorune.pl/u/$NAME.png" | xclip -selection clipboard && notify-send "Screenshot has been updated" diff --git a/.local/bin/set-wallpaper b/.local/bin/set-wallpaper new file mode 100755 index 0000000..daddc39 --- /dev/null +++ b/.local/bin/set-wallpaper @@ -0,0 +1,7 @@ +#!/bin/bash + +cp $1 ~/.wall.jpg +#feh --bg-scale /home/yorune/.wall.jpg +feh --bg-fill /home/yorune/.wall.jpg +#wal -i $1 -o wal-set +notify-send "Wallpaper changed" diff --git a/.local/bin/shut-sup-rest b/.local/bin/shut-sup-rest new file mode 100755 index 0000000..6384f9d --- /dev/null +++ b/.local/bin/shut-sup-rest @@ -0,0 +1,11 @@ +#!/bin/bash +choices="Lock\nSuspend\nReboot\nShutdown" + +chosen=$(echo -e "$choices" | dmenu -i) + +case "$chosen" in + Lock) slock ;; + Suspend) sudo s2ram && slock ;; + Reboot) sudo reboot ;; + Shutdown) sudo shutdown -h now ;; +esac diff --git a/.local/bin/speedtest-cli b/.local/bin/speedtest-cli new file mode 100755 index 0000000..92a2be0 --- /dev/null +++ b/.local/bin/speedtest-cli @@ -0,0 +1,2000 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright 2012 Matt Martz +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import re +import csv +import sys +import math +import errno +import signal +import socket +import timeit +import datetime +import platform +import threading +import xml.parsers.expat + +try: + import gzip + GZIP_BASE = gzip.GzipFile +except ImportError: + gzip = None + GZIP_BASE = object + +__version__ = '2.1.2' + + +class FakeShutdownEvent(object): + """Class to fake a threading.Event.isSet so that users of this module + are not required to register their own threading.Event() + """ + + @staticmethod + def isSet(): + "Dummy method to always return false""" + return False + + +# Some global variables we use +DEBUG = False +_GLOBAL_DEFAULT_TIMEOUT = object() +PY25PLUS = sys.version_info[:2] >= (2, 5) +PY26PLUS = sys.version_info[:2] >= (2, 6) +PY32PLUS = sys.version_info[:2] >= (3, 2) + +# Begin import game to handle Python 2 and Python 3 +try: + import json +except ImportError: + try: + import simplejson as json + except ImportError: + json = None + +try: + import xml.etree.ElementTree as ET + try: + from xml.etree.ElementTree import _Element as ET_Element + except ImportError: + pass +except ImportError: + from xml.dom import minidom as DOM + from xml.parsers.expat import ExpatError + ET = None + +try: + from urllib2 import (urlopen, Request, HTTPError, URLError, + AbstractHTTPHandler, ProxyHandler, + HTTPDefaultErrorHandler, HTTPRedirectHandler, + HTTPErrorProcessor, OpenerDirector) +except ImportError: + from urllib.request import (urlopen, Request, HTTPError, URLError, + AbstractHTTPHandler, ProxyHandler, + HTTPDefaultErrorHandler, HTTPRedirectHandler, + HTTPErrorProcessor, OpenerDirector) + +try: + from httplib import HTTPConnection, BadStatusLine +except ImportError: + from http.client import HTTPConnection, BadStatusLine + +try: + from httplib import HTTPSConnection +except ImportError: + try: + from http.client import HTTPSConnection + except ImportError: + HTTPSConnection = None + +try: + from httplib import FakeSocket +except ImportError: + FakeSocket = None + +try: + from Queue import Queue +except ImportError: + from queue import Queue + +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse + +try: + from urlparse import parse_qs +except ImportError: + try: + from urllib.parse import parse_qs + except ImportError: + from cgi import parse_qs + +try: + from hashlib import md5 +except ImportError: + from md5 import md5 + +try: + from argparse import ArgumentParser as ArgParser + from argparse import SUPPRESS as ARG_SUPPRESS + PARSER_TYPE_INT = int + PARSER_TYPE_STR = str + PARSER_TYPE_FLOAT = float +except ImportError: + from optparse import OptionParser as ArgParser + from optparse import SUPPRESS_HELP as ARG_SUPPRESS + PARSER_TYPE_INT = 'int' + PARSER_TYPE_STR = 'string' + PARSER_TYPE_FLOAT = 'float' + +try: + from cStringIO import StringIO + BytesIO = None +except ImportError: + try: + from StringIO import StringIO + BytesIO = None + except ImportError: + from io import StringIO, BytesIO + +try: + import __builtin__ +except ImportError: + import builtins + from io import TextIOWrapper, FileIO + + class _Py3Utf8Output(TextIOWrapper): + """UTF-8 encoded wrapper around stdout for py3, to override + ASCII stdout + """ + def __init__(self, f, **kwargs): + buf = FileIO(f.fileno(), 'w') + super(_Py3Utf8Output, self).__init__( + buf, + encoding='utf8', + errors='strict' + ) + + def write(self, s): + super(_Py3Utf8Output, self).write(s) + self.flush() + + _py3_print = getattr(builtins, 'print') + try: + _py3_utf8_stdout = _Py3Utf8Output(sys.stdout) + _py3_utf8_stderr = _Py3Utf8Output(sys.stderr) + except OSError: + # sys.stdout/sys.stderr is not a compatible stdout/stderr object + # just use it and hope things go ok + _py3_utf8_stdout = sys.stdout + _py3_utf8_stderr = sys.stderr + + def to_utf8(v): + """No-op encode to utf-8 for py3""" + return v + + def print_(*args, **kwargs): + """Wrapper function for py3 to print, with a utf-8 encoded stdout""" + if kwargs.get('file') == sys.stderr: + kwargs['file'] = _py3_utf8_stderr + else: + kwargs['file'] = kwargs.get('file', _py3_utf8_stdout) + _py3_print(*args, **kwargs) +else: + del __builtin__ + + def to_utf8(v): + """Encode value to utf-8 if possible for py2""" + try: + return v.encode('utf8', 'strict') + except AttributeError: + return v + + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5. + + Taken from https://pypi.python.org/pypi/six/ + + Modified to set encoding to UTF-8 always, and to flush after write + """ + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + encoding = 'utf8' # Always trust UTF-8 for output + if (isinstance(fp, file) and + isinstance(data, unicode) and + encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(encoding, errors) + fp.write(data) + fp.flush() + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) + +if PY32PLUS: + etree_iter = ET.Element.iter +elif PY25PLUS: + etree_iter = ET_Element.getiterator + +if PY26PLUS: + thread_is_alive = threading.Thread.is_alive +else: + thread_is_alive = threading.Thread.isAlive + + +# Exception "constants" to support Python 2 through Python 3 +try: + import ssl + try: + CERT_ERROR = (ssl.CertificateError,) + except AttributeError: + CERT_ERROR = tuple() + + HTTP_ERRORS = ( + (HTTPError, URLError, socket.error, ssl.SSLError, BadStatusLine) + + CERT_ERROR + ) +except ImportError: + ssl = None + HTTP_ERRORS = (HTTPError, URLError, socket.error, BadStatusLine) + + +class SpeedtestException(Exception): + """Base exception for this module""" + + +class SpeedtestCLIError(SpeedtestException): + """Generic exception for raising errors during CLI operation""" + + +class SpeedtestHTTPError(SpeedtestException): + """Base HTTP exception for this module""" + + +class SpeedtestConfigError(SpeedtestException): + """Configuration XML is invalid""" + + +class SpeedtestServersError(SpeedtestException): + """Servers XML is invalid""" + + +class ConfigRetrievalError(SpeedtestHTTPError): + """Could not retrieve config.php""" + + +class ServersRetrievalError(SpeedtestHTTPError): + """Could not retrieve speedtest-servers.php""" + + +class InvalidServerIDType(SpeedtestException): + """Server ID used for filtering was not an integer""" + + +class NoMatchedServers(SpeedtestException): + """No servers matched when filtering""" + + +class SpeedtestMiniConnectFailure(SpeedtestException): + """Could not connect to the provided speedtest mini server""" + + +class InvalidSpeedtestMiniServer(SpeedtestException): + """Server provided as a speedtest mini server does not actually appear + to be a speedtest mini server + """ + + +class ShareResultsConnectFailure(SpeedtestException): + """Could not connect to speedtest.net API to POST results""" + + +class ShareResultsSubmitFailure(SpeedtestException): + """Unable to successfully POST results to speedtest.net API after + connection + """ + + +class SpeedtestUploadTimeout(SpeedtestException): + """testlength configuration reached during upload + Used to ensure the upload halts when no additional data should be sent + """ + + +class SpeedtestBestServerFailure(SpeedtestException): + """Unable to determine best server""" + + +class SpeedtestMissingBestServer(SpeedtestException): + """get_best_server not called or not able to determine best server""" + + +def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, + source_address=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + + Largely vendored from Python 2.7, modified to work with Python 2.4 + """ + + host, port = address + err = None + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + if timeout is not _GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(float(timeout)) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error: + err = get_exception() + if sock is not None: + sock.close() + + if err is not None: + raise err + else: + raise socket.error("getaddrinfo returns an empty list") + + +class SpeedtestHTTPConnection(HTTPConnection): + """Custom HTTPConnection to support source_address across + Python 2.4 - Python 3 + """ + def __init__(self, *args, **kwargs): + source_address = kwargs.pop('source_address', None) + timeout = kwargs.pop('timeout', 10) + + self._tunnel_host = None + + HTTPConnection.__init__(self, *args, **kwargs) + + self.source_address = source_address + self.timeout = timeout + + def connect(self): + """Connect to the host and port specified in __init__.""" + try: + self.sock = socket.create_connection( + (self.host, self.port), + self.timeout, + self.source_address + ) + except (AttributeError, TypeError): + self.sock = create_connection( + (self.host, self.port), + self.timeout, + self.source_address + ) + + if self._tunnel_host: + self._tunnel() + + +if HTTPSConnection: + class SpeedtestHTTPSConnection(HTTPSConnection): + """Custom HTTPSConnection to support source_address across + Python 2.4 - Python 3 + """ + default_port = 443 + + def __init__(self, *args, **kwargs): + source_address = kwargs.pop('source_address', None) + timeout = kwargs.pop('timeout', 10) + + self._tunnel_host = None + + HTTPSConnection.__init__(self, *args, **kwargs) + + self.timeout = timeout + self.source_address = source_address + + def connect(self): + "Connect to a host on a given (SSL) port." + try: + self.sock = socket.create_connection( + (self.host, self.port), + self.timeout, + self.source_address + ) + except (AttributeError, TypeError): + self.sock = create_connection( + (self.host, self.port), + self.timeout, + self.source_address + ) + + if self._tunnel_host: + self._tunnel() + + if ssl: + try: + kwargs = {} + if hasattr(ssl, 'SSLContext'): + if self._tunnel_host: + kwargs['server_hostname'] = self._tunnel_host + else: + kwargs['server_hostname'] = self.host + self.sock = self._context.wrap_socket(self.sock, **kwargs) + except AttributeError: + self.sock = ssl.wrap_socket(self.sock) + try: + self.sock.server_hostname = self.host + except AttributeError: + pass + elif FakeSocket: + # Python 2.4/2.5 support + try: + self.sock = FakeSocket(self.sock, socket.ssl(self.sock)) + except AttributeError: + raise SpeedtestException( + 'This version of Python does not support HTTPS/SSL ' + 'functionality' + ) + else: + raise SpeedtestException( + 'This version of Python does not support HTTPS/SSL ' + 'functionality' + ) + + +def _build_connection(connection, source_address, timeout, context=None): + """Cross Python 2.4 - Python 3 callable to build an ``HTTPConnection`` or + ``HTTPSConnection`` with the args we need + + Called from ``http(s)_open`` methods of ``SpeedtestHTTPHandler`` or + ``SpeedtestHTTPSHandler`` + """ + def inner(host, **kwargs): + kwargs.update({ + 'source_address': source_address, + 'timeout': timeout + }) + if context: + kwargs['context'] = context + return connection(host, **kwargs) + return inner + + +class SpeedtestHTTPHandler(AbstractHTTPHandler): + """Custom ``HTTPHandler`` that can build a ``HTTPConnection`` with the + args we need for ``source_address`` and ``timeout`` + """ + def __init__(self, debuglevel=0, source_address=None, timeout=10): + AbstractHTTPHandler.__init__(self, debuglevel) + self.source_address = source_address + self.timeout = timeout + + def http_open(self, req): + return self.do_open( + _build_connection( + SpeedtestHTTPConnection, + self.source_address, + self.timeout + ), + req + ) + + http_request = AbstractHTTPHandler.do_request_ + + +class SpeedtestHTTPSHandler(AbstractHTTPHandler): + """Custom ``HTTPSHandler`` that can build a ``HTTPSConnection`` with the + args we need for ``source_address`` and ``timeout`` + """ + def __init__(self, debuglevel=0, context=None, source_address=None, + timeout=10): + AbstractHTTPHandler.__init__(self, debuglevel) + self._context = context + self.source_address = source_address + self.timeout = timeout + + def https_open(self, req): + return self.do_open( + _build_connection( + SpeedtestHTTPSConnection, + self.source_address, + self.timeout, + context=self._context, + ), + req + ) + + https_request = AbstractHTTPHandler.do_request_ + + +def build_opener(source_address=None, timeout=10): + """Function similar to ``urllib2.build_opener`` that will build + an ``OpenerDirector`` with the explicit handlers we want, + ``source_address`` for binding, ``timeout`` and our custom + `User-Agent` + """ + + printer('Timeout set to %d' % timeout, debug=True) + + if source_address: + source_address_tuple = (source_address, 0) + printer('Binding to source address: %r' % (source_address_tuple,), + debug=True) + else: + source_address_tuple = None + + handlers = [ + ProxyHandler(), + SpeedtestHTTPHandler(source_address=source_address_tuple, + timeout=timeout), + SpeedtestHTTPSHandler(source_address=source_address_tuple, + timeout=timeout), + HTTPDefaultErrorHandler(), + HTTPRedirectHandler(), + HTTPErrorProcessor() + ] + + opener = OpenerDirector() + opener.addheaders = [('User-agent', build_user_agent())] + + for handler in handlers: + opener.add_handler(handler) + + return opener + + +class GzipDecodedResponse(GZIP_BASE): + """A file-like object to decode a response encoded with the gzip + method, as described in RFC 1952. + + Largely copied from ``xmlrpclib``/``xmlrpc.client`` and modified + to work for py2.4-py3 + """ + def __init__(self, response): + # response doesn't support tell() and read(), required by + # GzipFile + if not gzip: + raise SpeedtestHTTPError('HTTP response body is gzip encoded, ' + 'but gzip support is not available') + IO = BytesIO or StringIO + self.io = IO() + while 1: + chunk = response.read(1024) + if len(chunk) == 0: + break + self.io.write(chunk) + self.io.seek(0) + gzip.GzipFile.__init__(self, mode='rb', fileobj=self.io) + + def close(self): + try: + gzip.GzipFile.close(self) + finally: + self.io.close() + + +def get_exception(): + """Helper function to work with py2.4-py3 for getting the current + exception in a try/except block + """ + return sys.exc_info()[1] + + +def distance(origin, destination): + """Determine distance between 2 sets of [lat,lon] in km""" + + lat1, lon1 = origin + lat2, lon2 = destination + radius = 6371 # km + + dlat = math.radians(lat2 - lat1) + dlon = math.radians(lon2 - lon1) + a = (math.sin(dlat / 2) * math.sin(dlat / 2) + + math.cos(math.radians(lat1)) * + math.cos(math.radians(lat2)) * math.sin(dlon / 2) * + math.sin(dlon / 2)) + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) + d = radius * c + + return d + + +def build_user_agent(): + """Build a Mozilla/5.0 compatible User-Agent string""" + + ua_tuple = ( + 'Mozilla/5.0', + '(%s; U; %s; en-us)' % (platform.platform(), + platform.architecture()[0]), + 'Python/%s' % platform.python_version(), + '(KHTML, like Gecko)', + 'speedtest-cli/%s' % __version__ + ) + user_agent = ' '.join(ua_tuple) + printer('User-Agent: %s' % user_agent, debug=True) + return user_agent + + +def build_request(url, data=None, headers=None, bump='0', secure=False): + """Build a urllib2 request object + + This function automatically adds a User-Agent header to all requests + + """ + + if not headers: + headers = {} + + if url[0] == ':': + scheme = ('http', 'https')[bool(secure)] + schemed_url = '%s%s' % (scheme, url) + else: + schemed_url = url + + if '?' in url: + delim = '&' + else: + delim = '?' + + # WHO YOU GONNA CALL? CACHE BUSTERS! + final_url = '%s%sx=%s.%s' % (schemed_url, delim, + int(timeit.time.time() * 1000), + bump) + + headers.update({ + 'Cache-Control': 'no-cache', + }) + + printer('%s %s' % (('GET', 'POST')[bool(data)], final_url), + debug=True) + + return Request(final_url, data=data, headers=headers) + + +def catch_request(request, opener=None): + """Helper function to catch common exceptions encountered when + establishing a connection with a HTTP/HTTPS request + + """ + + if opener: + _open = opener.open + else: + _open = urlopen + + try: + uh = _open(request) + if request.get_full_url() != uh.geturl(): + printer('Redirected to %s' % uh.geturl(), debug=True) + return uh, False + except HTTP_ERRORS: + e = get_exception() + return None, e + + +def get_response_stream(response): + """Helper function to return either a Gzip reader if + ``Content-Encoding`` is ``gzip`` otherwise the response itself + + """ + + try: + getheader = response.headers.getheader + except AttributeError: + getheader = response.getheader + + if getheader('content-encoding') == 'gzip': + return GzipDecodedResponse(response) + + return response + + +def get_attributes_by_tag_name(dom, tag_name): + """Retrieve an attribute from an XML document and return it in a + consistent format + + Only used with xml.dom.minidom, which is likely only to be used + with python versions older than 2.5 + """ + elem = dom.getElementsByTagName(tag_name)[0] + return dict(list(elem.attributes.items())) + + +def print_dots(shutdown_event): + """Built in callback function used by Thread classes for printing + status + """ + def inner(current, total, start=False, end=False): + if shutdown_event.isSet(): + return + + sys.stdout.write('.') + if current + 1 == total and end is True: + sys.stdout.write('\n') + sys.stdout.flush() + return inner + + +def do_nothing(*args, **kwargs): + pass + + +class HTTPDownloader(threading.Thread): + """Thread class for retrieving a URL""" + + def __init__(self, i, request, start, timeout, opener=None, + shutdown_event=None): + threading.Thread.__init__(self) + self.request = request + self.result = [0] + self.starttime = start + self.timeout = timeout + self.i = i + if opener: + self._opener = opener.open + else: + self._opener = urlopen + + if shutdown_event: + self._shutdown_event = shutdown_event + else: + self._shutdown_event = FakeShutdownEvent() + + def run(self): + try: + if (timeit.default_timer() - self.starttime) <= self.timeout: + f = self._opener(self.request) + while (not self._shutdown_event.isSet() and + (timeit.default_timer() - self.starttime) <= + self.timeout): + self.result.append(len(f.read(10240))) + if self.result[-1] == 0: + break + f.close() + except IOError: + pass + + +class HTTPUploaderData(object): + """File like object to improve cutting off the upload once the timeout + has been reached + """ + + def __init__(self, length, start, timeout, shutdown_event=None): + self.length = length + self.start = start + self.timeout = timeout + + if shutdown_event: + self._shutdown_event = shutdown_event + else: + self._shutdown_event = FakeShutdownEvent() + + self._data = None + + self.total = [0] + + def pre_allocate(self): + chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + multiplier = int(round(int(self.length) / 36.0)) + IO = BytesIO or StringIO + try: + self._data = IO( + ('content1=%s' % + (chars * multiplier)[0:int(self.length) - 9] + ).encode() + ) + except MemoryError: + raise SpeedtestCLIError( + 'Insufficient memory to pre-allocate upload data. Please ' + 'use --no-pre-allocate' + ) + + @property + def data(self): + if not self._data: + self.pre_allocate() + return self._data + + def read(self, n=10240): + if ((timeit.default_timer() - self.start) <= self.timeout and + not self._shutdown_event.isSet()): + chunk = self.data.read(n) + self.total.append(len(chunk)) + return chunk + else: + raise SpeedtestUploadTimeout() + + def __len__(self): + return self.length + + +class HTTPUploader(threading.Thread): + """Thread class for putting a URL""" + + def __init__(self, i, request, start, size, timeout, opener=None, + shutdown_event=None): + threading.Thread.__init__(self) + self.request = request + self.request.data.start = self.starttime = start + self.size = size + self.result = None + self.timeout = timeout + self.i = i + + if opener: + self._opener = opener.open + else: + self._opener = urlopen + + if shutdown_event: + self._shutdown_event = shutdown_event + else: + self._shutdown_event = FakeShutdownEvent() + + def run(self): + request = self.request + try: + if ((timeit.default_timer() - self.starttime) <= self.timeout and + not self._shutdown_event.isSet()): + try: + f = self._opener(request) + except TypeError: + # PY24 expects a string or buffer + # This also causes issues with Ctrl-C, but we will concede + # for the moment that Ctrl-C on PY24 isn't immediate + request = build_request(self.request.get_full_url(), + data=request.data.read(self.size)) + f = self._opener(request) + f.read(11) + f.close() + self.result = sum(self.request.data.total) + else: + self.result = 0 + except (IOError, SpeedtestUploadTimeout): + self.result = sum(self.request.data.total) + + +class SpeedtestResults(object): + """Class for holding the results of a speedtest, including: + + Download speed + Upload speed + Ping/Latency to test server + Data about server that the test was run against + + Additionally this class can return a result data as a dictionary or CSV, + as well as submit a POST of the result data to the speedtest.net API + to get a share results image link. + """ + + def __init__(self, download=0, upload=0, ping=0, server=None, client=None, + opener=None, secure=False): + self.download = download + self.upload = upload + self.ping = ping + if server is None: + self.server = {} + else: + self.server = server + self.client = client or {} + + self._share = None + self.timestamp = '%sZ' % datetime.datetime.utcnow().isoformat() + self.bytes_received = 0 + self.bytes_sent = 0 + + if opener: + self._opener = opener + else: + self._opener = build_opener() + + self._secure = secure + + def __repr__(self): + return repr(self.dict()) + + def share(self): + """POST data to the speedtest.net API to obtain a share results + link + """ + + if self._share: + return self._share + + download = int(round(self.download / 1000.0, 0)) + ping = int(round(self.ping, 0)) + upload = int(round(self.upload / 1000.0, 0)) + + # Build the request to send results back to speedtest.net + # We use a list instead of a dict because the API expects parameters + # in a certain order + api_data = [ + 'recommendedserverid=%s' % self.server['id'], + 'ping=%s' % ping, + 'screenresolution=', + 'promo=', + 'download=%s' % download, + 'screendpi=', + 'upload=%s' % upload, + 'testmethod=http', + 'hash=%s' % md5(('%s-%s-%s-%s' % + (ping, upload, download, '297aae72')) + .encode()).hexdigest(), + 'touchscreen=none', + 'startmode=pingselect', + 'accuracy=1', + 'bytesreceived=%s' % self.bytes_received, + 'bytessent=%s' % self.bytes_sent, + 'serverid=%s' % self.server['id'], + ] + + headers = {'Referer': 'http://c.speedtest.net/flash/speedtest.swf'} + request = build_request('://www.speedtest.net/api/api.php', + data='&'.join(api_data).encode(), + headers=headers, secure=self._secure) + f, e = catch_request(request, opener=self._opener) + if e: + raise ShareResultsConnectFailure(e) + + response = f.read() + code = f.code + f.close() + + if int(code) != 200: + raise ShareResultsSubmitFailure('Could not submit results to ' + 'speedtest.net') + + qsargs = parse_qs(response.decode()) + resultid = qsargs.get('resultid') + if not resultid or len(resultid) != 1: + raise ShareResultsSubmitFailure('Could not submit results to ' + 'speedtest.net') + + self._share = 'http://www.speedtest.net/result/%s.png' % resultid[0] + + return self._share + + def dict(self): + """Return dictionary of result data""" + + return { + 'download': self.download, + 'upload': self.upload, + 'ping': self.ping, + 'server': self.server, + 'timestamp': self.timestamp, + 'bytes_sent': self.bytes_sent, + 'bytes_received': self.bytes_received, + 'share': self._share, + 'client': self.client, + } + + @staticmethod + def csv_header(delimiter=','): + """Return CSV Headers""" + + row = ['Server ID', 'Sponsor', 'Server Name', 'Timestamp', 'Distance', + 'Ping', 'Download', 'Upload', 'Share', 'IP Address'] + out = StringIO() + writer = csv.writer(out, delimiter=delimiter, lineterminator='') + writer.writerow([to_utf8(v) for v in row]) + return out.getvalue() + + def csv(self, delimiter=','): + """Return data in CSV format""" + + data = self.dict() + out = StringIO() + writer = csv.writer(out, delimiter=delimiter, lineterminator='') + row = [data['server']['id'], data['server']['sponsor'], + data['server']['name'], data['timestamp'], + data['server']['d'], data['ping'], data['download'], + data['upload'], self._share or '', self.client['ip']] + writer.writerow([to_utf8(v) for v in row]) + return out.getvalue() + + def json(self, pretty=False): + """Return data in JSON format""" + + kwargs = {} + if pretty: + kwargs.update({ + 'indent': 4, + 'sort_keys': True + }) + return json.dumps(self.dict(), **kwargs) + + +class Speedtest(object): + """Class for performing standard speedtest.net testing operations""" + + def __init__(self, config=None, source_address=None, timeout=10, + secure=False, shutdown_event=None): + self.config = {} + + self._source_address = source_address + self._timeout = timeout + self._opener = build_opener(source_address, timeout) + + self._secure = secure + + if shutdown_event: + self._shutdown_event = shutdown_event + else: + self._shutdown_event = FakeShutdownEvent() + + self.get_config() + if config is not None: + self.config.update(config) + + self.servers = {} + self.closest = [] + self._best = {} + + self.results = SpeedtestResults( + client=self.config['client'], + opener=self._opener, + secure=secure, + ) + + @property + def best(self): + if not self._best: + self.get_best_server() + return self._best + + def get_config(self): + """Download the speedtest.net configuration and return only the data + we are interested in + """ + + headers = {} + if gzip: + headers['Accept-Encoding'] = 'gzip' + request = build_request('://www.speedtest.net/speedtest-config.php', + headers=headers, secure=self._secure) + uh, e = catch_request(request, opener=self._opener) + if e: + raise ConfigRetrievalError(e) + configxml_list = [] + + stream = get_response_stream(uh) + + while 1: + try: + configxml_list.append(stream.read(1024)) + except (OSError, EOFError): + raise ConfigRetrievalError(get_exception()) + if len(configxml_list[-1]) == 0: + break + stream.close() + uh.close() + + if int(uh.code) != 200: + return None + + configxml = ''.encode().join(configxml_list) + + printer('Config XML:\n%s' % configxml, debug=True) + + try: + try: + root = ET.fromstring(configxml) + except ET.ParseError: + e = get_exception() + raise SpeedtestConfigError( + 'Malformed speedtest.net configuration: %s' % e + ) + server_config = root.find('server-config').attrib + download = root.find('download').attrib + upload = root.find('upload').attrib + # times = root.find('times').attrib + client = root.find('client').attrib + + except AttributeError: + try: + root = DOM.parseString(configxml) + except ExpatError: + e = get_exception() + raise SpeedtestConfigError( + 'Malformed speedtest.net configuration: %s' % e + ) + server_config = get_attributes_by_tag_name(root, 'server-config') + download = get_attributes_by_tag_name(root, 'download') + upload = get_attributes_by_tag_name(root, 'upload') + # times = get_attributes_by_tag_name(root, 'times') + client = get_attributes_by_tag_name(root, 'client') + + ignore_servers = list( + map(int, server_config['ignoreids'].split(',')) + ) + + ratio = int(upload['ratio']) + upload_max = int(upload['maxchunkcount']) + up_sizes = [32768, 65536, 131072, 262144, 524288, 1048576, 7340032] + sizes = { + 'upload': up_sizes[ratio - 1:], + 'download': [350, 500, 750, 1000, 1500, 2000, 2500, + 3000, 3500, 4000] + } + + size_count = len(sizes['upload']) + + upload_count = int(math.ceil(upload_max / size_count)) + + counts = { + 'upload': upload_count, + 'download': int(download['threadsperurl']) + } + + threads = { + 'upload': int(upload['threads']), + 'download': int(server_config['threadcount']) * 2 + } + + length = { + 'upload': int(upload['testlength']), + 'download': int(download['testlength']) + } + + self.config.update({ + 'client': client, + 'ignore_servers': ignore_servers, + 'sizes': sizes, + 'counts': counts, + 'threads': threads, + 'length': length, + 'upload_max': upload_count * size_count + }) + + try: + self.lat_lon = (float(client['lat']), float(client['lon'])) + except ValueError: + raise SpeedtestConfigError( + 'Unknown location: lat=%r lon=%r' % + (client.get('lat'), client.get('lon')) + ) + + printer('Config:\n%r' % self.config, debug=True) + + return self.config + + def get_servers(self, servers=None, exclude=None): + """Retrieve a the list of speedtest.net servers, optionally filtered + to servers matching those specified in the ``servers`` argument + """ + if servers is None: + servers = [] + + if exclude is None: + exclude = [] + + self.servers.clear() + + for server_list in (servers, exclude): + for i, s in enumerate(server_list): + try: + server_list[i] = int(s) + except ValueError: + raise InvalidServerIDType( + '%s is an invalid server type, must be int' % s + ) + + urls = [ + '://www.speedtest.net/speedtest-servers-static.php', + 'http://c.speedtest.net/speedtest-servers-static.php', + '://www.speedtest.net/speedtest-servers.php', + 'http://c.speedtest.net/speedtest-servers.php', + ] + + headers = {} + if gzip: + headers['Accept-Encoding'] = 'gzip' + + errors = [] + for url in urls: + try: + request = build_request( + '%s?threads=%s' % (url, + self.config['threads']['download']), + headers=headers, + secure=self._secure + ) + uh, e = catch_request(request, opener=self._opener) + if e: + errors.append('%s' % e) + raise ServersRetrievalError() + + stream = get_response_stream(uh) + + serversxml_list = [] + while 1: + try: + serversxml_list.append(stream.read(1024)) + except (OSError, EOFError): + raise ServersRetrievalError(get_exception()) + if len(serversxml_list[-1]) == 0: + break + + stream.close() + uh.close() + + if int(uh.code) != 200: + raise ServersRetrievalError() + + serversxml = ''.encode().join(serversxml_list) + + printer('Servers XML:\n%s' % serversxml, debug=True) + + try: + try: + try: + root = ET.fromstring(serversxml) + except ET.ParseError: + e = get_exception() + raise SpeedtestServersError( + 'Malformed speedtest.net server list: %s' % e + ) + elements = etree_iter(root, 'server') + except AttributeError: + try: + root = DOM.parseString(serversxml) + except ExpatError: + e = get_exception() + raise SpeedtestServersError( + 'Malformed speedtest.net server list: %s' % e + ) + elements = root.getElementsByTagName('server') + except (SyntaxError, xml.parsers.expat.ExpatError): + raise ServersRetrievalError() + + for server in elements: + try: + attrib = server.attrib + except AttributeError: + attrib = dict(list(server.attributes.items())) + + if servers and int(attrib.get('id')) not in servers: + continue + + if (int(attrib.get('id')) in self.config['ignore_servers'] + or int(attrib.get('id')) in exclude): + continue + + try: + d = distance(self.lat_lon, + (float(attrib.get('lat')), + float(attrib.get('lon')))) + except Exception: + continue + + attrib['d'] = d + + try: + self.servers[d].append(attrib) + except KeyError: + self.servers[d] = [attrib] + + break + + except ServersRetrievalError: + continue + + if (servers or exclude) and not self.servers: + raise NoMatchedServers() + + return self.servers + + def set_mini_server(self, server): + """Instead of querying for a list of servers, set a link to a + speedtest mini server + """ + + urlparts = urlparse(server) + + name, ext = os.path.splitext(urlparts[2]) + if ext: + url = os.path.dirname(server) + else: + url = server + + request = build_request(url) + uh, e = catch_request(request, opener=self._opener) + if e: + raise SpeedtestMiniConnectFailure('Failed to connect to %s' % + server) + else: + text = uh.read() + uh.close() + + extension = re.findall('upload_?[Ee]xtension: "([^"]+)"', + text.decode()) + if not extension: + for ext in ['php', 'asp', 'aspx', 'jsp']: + try: + f = self._opener.open( + '%s/speedtest/upload.%s' % (url, ext) + ) + except Exception: + pass + else: + data = f.read().strip().decode() + if (f.code == 200 and + len(data.splitlines()) == 1 and + re.match('size=[0-9]', data)): + extension = [ext] + break + if not urlparts or not extension: + raise InvalidSpeedtestMiniServer('Invalid Speedtest Mini Server: ' + '%s' % server) + + self.servers = [{ + 'sponsor': 'Speedtest Mini', + 'name': urlparts[1], + 'd': 0, + 'url': '%s/speedtest/upload.%s' % (url.rstrip('/'), extension[0]), + 'latency': 0, + 'id': 0 + }] + + return self.servers + + def get_closest_servers(self, limit=5): + """Limit servers to the closest speedtest.net servers based on + geographic distance + """ + + if not self.servers: + self.get_servers() + + for d in sorted(self.servers.keys()): + for s in self.servers[d]: + self.closest.append(s) + if len(self.closest) == limit: + break + else: + continue + break + + printer('Closest Servers:\n%r' % self.closest, debug=True) + return self.closest + + def get_best_server(self, servers=None): + """Perform a speedtest.net "ping" to determine which speedtest.net + server has the lowest latency + """ + + if not servers: + if not self.closest: + servers = self.get_closest_servers() + servers = self.closest + + if self._source_address: + source_address_tuple = (self._source_address, 0) + else: + source_address_tuple = None + + user_agent = build_user_agent() + + results = {} + for server in servers: + cum = [] + url = os.path.dirname(server['url']) + stamp = int(timeit.time.time() * 1000) + latency_url = '%s/latency.txt?x=%s' % (url, stamp) + for i in range(0, 3): + this_latency_url = '%s.%s' % (latency_url, i) + printer('%s %s' % ('GET', this_latency_url), + debug=True) + urlparts = urlparse(latency_url) + try: + if urlparts[0] == 'https': + h = SpeedtestHTTPSConnection( + urlparts[1], + source_address=source_address_tuple + ) + else: + h = SpeedtestHTTPConnection( + urlparts[1], + source_address=source_address_tuple + ) + headers = {'User-Agent': user_agent} + path = '%s?%s' % (urlparts[2], urlparts[4]) + start = timeit.default_timer() + h.request("GET", path, headers=headers) + r = h.getresponse() + total = (timeit.default_timer() - start) + except HTTP_ERRORS: + e = get_exception() + printer('ERROR: %r' % e, debug=True) + cum.append(3600) + continue + + text = r.read(9) + if int(r.status) == 200 and text == 'test=test'.encode(): + cum.append(total) + else: + cum.append(3600) + h.close() + + avg = round((sum(cum) / 6) * 1000.0, 3) + results[avg] = server + + try: + fastest = sorted(results.keys())[0] + except IndexError: + raise SpeedtestBestServerFailure('Unable to connect to servers to ' + 'test latency.') + best = results[fastest] + best['latency'] = fastest + + self.results.ping = fastest + self.results.server = best + + self._best.update(best) + printer('Best Server:\n%r' % best, debug=True) + return best + + def download(self, callback=do_nothing, threads=None): + """Test download speed against speedtest.net + + A ``threads`` value of ``None`` will fall back to those dictated + by the speedtest.net configuration + """ + + urls = [] + for size in self.config['sizes']['download']: + for _ in range(0, self.config['counts']['download']): + urls.append('%s/random%sx%s.jpg' % + (os.path.dirname(self.best['url']), size, size)) + + request_count = len(urls) + requests = [] + for i, url in enumerate(urls): + requests.append( + build_request(url, bump=i, secure=self._secure) + ) + + max_threads = threads or self.config['threads']['download'] + in_flight = {'threads': 0} + + def producer(q, requests, request_count): + for i, request in enumerate(requests): + thread = HTTPDownloader( + i, + request, + start, + self.config['length']['download'], + opener=self._opener, + shutdown_event=self._shutdown_event + ) + while in_flight['threads'] >= max_threads: + timeit.time.sleep(0.001) + thread.start() + q.put(thread, True) + in_flight['threads'] += 1 + callback(i, request_count, start=True) + + finished = [] + + def consumer(q, request_count): + _is_alive = thread_is_alive + while len(finished) < request_count: + thread = q.get(True) + while _is_alive(thread): + thread.join(timeout=0.001) + in_flight['threads'] -= 1 + finished.append(sum(thread.result)) + callback(thread.i, request_count, end=True) + + q = Queue(max_threads) + prod_thread = threading.Thread(target=producer, + args=(q, requests, request_count)) + cons_thread = threading.Thread(target=consumer, + args=(q, request_count)) + start = timeit.default_timer() + prod_thread.start() + cons_thread.start() + _is_alive = thread_is_alive + while _is_alive(prod_thread): + prod_thread.join(timeout=0.001) + while _is_alive(cons_thread): + cons_thread.join(timeout=0.001) + + stop = timeit.default_timer() + self.results.bytes_received = sum(finished) + self.results.download = ( + (self.results.bytes_received / (stop - start)) * 8.0 + ) + if self.results.download > 100000: + self.config['threads']['upload'] = 8 + return self.results.download + + def upload(self, callback=do_nothing, pre_allocate=True, threads=None): + """Test upload speed against speedtest.net + + A ``threads`` value of ``None`` will fall back to those dictated + by the speedtest.net configuration + """ + + sizes = [] + + for size in self.config['sizes']['upload']: + for _ in range(0, self.config['counts']['upload']): + sizes.append(size) + + # request_count = len(sizes) + request_count = self.config['upload_max'] + + requests = [] + for i, size in enumerate(sizes): + # We set ``0`` for ``start`` and handle setting the actual + # ``start`` in ``HTTPUploader`` to get better measurements + data = HTTPUploaderData( + size, + 0, + self.config['length']['upload'], + shutdown_event=self._shutdown_event + ) + if pre_allocate: + data.pre_allocate() + + headers = {'Content-length': size} + requests.append( + ( + build_request(self.best['url'], data, secure=self._secure, + headers=headers), + size + ) + ) + + max_threads = threads or self.config['threads']['upload'] + in_flight = {'threads': 0} + + def producer(q, requests, request_count): + for i, request in enumerate(requests[:request_count]): + thread = HTTPUploader( + i, + request[0], + start, + request[1], + self.config['length']['upload'], + opener=self._opener, + shutdown_event=self._shutdown_event + ) + while in_flight['threads'] >= max_threads: + timeit.time.sleep(0.001) + thread.start() + q.put(thread, True) + in_flight['threads'] += 1 + callback(i, request_count, start=True) + + finished = [] + + def consumer(q, request_count): + _is_alive = thread_is_alive + while len(finished) < request_count: + thread = q.get(True) + while _is_alive(thread): + thread.join(timeout=0.001) + in_flight['threads'] -= 1 + finished.append(thread.result) + callback(thread.i, request_count, end=True) + + q = Queue(threads or self.config['threads']['upload']) + prod_thread = threading.Thread(target=producer, + args=(q, requests, request_count)) + cons_thread = threading.Thread(target=consumer, + args=(q, request_count)) + start = timeit.default_timer() + prod_thread.start() + cons_thread.start() + _is_alive = thread_is_alive + while _is_alive(prod_thread): + prod_thread.join(timeout=0.1) + while _is_alive(cons_thread): + cons_thread.join(timeout=0.1) + + stop = timeit.default_timer() + self.results.bytes_sent = sum(finished) + self.results.upload = ( + (self.results.bytes_sent / (stop - start)) * 8.0 + ) + return self.results.upload + + +def ctrl_c(shutdown_event): + """Catch Ctrl-C key sequence and set a SHUTDOWN_EVENT for our threaded + operations + """ + def inner(signum, frame): + shutdown_event.set() + printer('\nCancelling...', error=True) + sys.exit(0) + return inner + + +def version(): + """Print the version""" + + printer('speedtest-cli %s' % __version__) + printer('Python %s' % sys.version.replace('\n', '')) + sys.exit(0) + + +def csv_header(delimiter=','): + """Print the CSV Headers""" + + printer(SpeedtestResults.csv_header(delimiter=delimiter)) + sys.exit(0) + + +def parse_args(): + """Function to handle building and parsing of command line arguments""" + description = ( + 'Command line interface for testing internet bandwidth using ' + 'speedtest.net.\n' + '------------------------------------------------------------' + '--------------\n' + 'https://github.com/sivel/speedtest-cli') + + parser = ArgParser(description=description) + # Give optparse.OptionParser an `add_argument` method for + # compatibility with argparse.ArgumentParser + try: + parser.add_argument = parser.add_option + except AttributeError: + pass + parser.add_argument('--no-download', dest='download', default=True, + action='store_const', const=False, + help='Do not perform download test') + parser.add_argument('--no-upload', dest='upload', default=True, + action='store_const', const=False, + help='Do not perform upload test') + parser.add_argument('--single', default=False, action='store_true', + help='Only use a single connection instead of ' + 'multiple. This simulates a typical file ' + 'transfer.') + parser.add_argument('--bytes', dest='units', action='store_const', + const=('byte', 8), default=('bit', 1), + help='Display values in bytes instead of bits. Does ' + 'not affect the image generated by --share, nor ' + 'output from --json or --csv') + parser.add_argument('--share', action='store_true', + help='Generate and provide a URL to the speedtest.net ' + 'share results image, not displayed with --csv') + parser.add_argument('--simple', action='store_true', default=False, + help='Suppress verbose output, only show basic ' + 'information') + parser.add_argument('--csv', action='store_true', default=False, + help='Suppress verbose output, only show basic ' + 'information in CSV format. Speeds listed in ' + 'bit/s and not affected by --bytes') + parser.add_argument('--csv-delimiter', default=',', type=PARSER_TYPE_STR, + help='Single character delimiter to use in CSV ' + 'output. Default ","') + parser.add_argument('--csv-header', action='store_true', default=False, + help='Print CSV headers') + parser.add_argument('--json', action='store_true', default=False, + help='Suppress verbose output, only show basic ' + 'information in JSON format. Speeds listed in ' + 'bit/s and not affected by --bytes') + parser.add_argument('--list', action='store_true', + help='Display a list of speedtest.net servers ' + 'sorted by distance') + parser.add_argument('--server', type=PARSER_TYPE_INT, action='append', + help='Specify a server ID to test against. Can be ' + 'supplied multiple times') + parser.add_argument('--exclude', type=PARSER_TYPE_INT, action='append', + help='Exclude a server from selection. Can be ' + 'supplied multiple times') + parser.add_argument('--mini', help='URL of the Speedtest Mini server') + parser.add_argument('--source', help='Source IP address to bind to') + parser.add_argument('--timeout', default=10, type=PARSER_TYPE_FLOAT, + help='HTTP timeout in seconds. Default 10') + parser.add_argument('--secure', action='store_true', + help='Use HTTPS instead of HTTP when communicating ' + 'with speedtest.net operated servers') + parser.add_argument('--no-pre-allocate', dest='pre_allocate', + action='store_const', default=True, const=False, + help='Do not pre allocate upload data. Pre allocation ' + 'is enabled by default to improve upload ' + 'performance. To support systems with ' + 'insufficient memory, use this option to avoid a ' + 'MemoryError') + parser.add_argument('--version', action='store_true', + help='Show the version number and exit') + parser.add_argument('--debug', action='store_true', + help=ARG_SUPPRESS, default=ARG_SUPPRESS) + + options = parser.parse_args() + if isinstance(options, tuple): + args = options[0] + else: + args = options + return args + + +def validate_optional_args(args): + """Check if an argument was provided that depends on a module that may + not be part of the Python standard library. + + If such an argument is supplied, and the module does not exist, exit + with an error stating which module is missing. + """ + optional_args = { + 'json': ('json/simplejson python module', json), + 'secure': ('SSL support', HTTPSConnection), + } + + for arg, info in optional_args.items(): + if getattr(args, arg, False) and info[1] is None: + raise SystemExit('%s is not installed. --%s is ' + 'unavailable' % (info[0], arg)) + + +def printer(string, quiet=False, debug=False, error=False, **kwargs): + """Helper function print a string with various features""" + + if debug and not DEBUG: + return + + if debug: + if sys.stdout.isatty(): + out = '\033[1;30mDEBUG: %s\033[0m' % string + else: + out = 'DEBUG: %s' % string + else: + out = string + + if error: + kwargs['file'] = sys.stderr + + if not quiet: + print_(out, **kwargs) + + +def shell(): + """Run the full speedtest.net test""" + + global DEBUG + shutdown_event = threading.Event() + + signal.signal(signal.SIGINT, ctrl_c(shutdown_event)) + + args = parse_args() + + # Print the version and exit + if args.version: + version() + + if not args.download and not args.upload: + raise SpeedtestCLIError('Cannot supply both --no-download and ' + '--no-upload') + + if len(args.csv_delimiter) != 1: + raise SpeedtestCLIError('--csv-delimiter must be a single character') + + if args.csv_header: + csv_header(args.csv_delimiter) + + validate_optional_args(args) + + debug = getattr(args, 'debug', False) + if debug == 'SUPPRESSHELP': + debug = False + if debug: + DEBUG = True + + if args.simple or args.csv or args.json: + quiet = True + else: + quiet = False + + if args.csv or args.json: + machine_format = True + else: + machine_format = False + + # Don't set a callback if we are running quietly + if quiet or debug: + callback = do_nothing + else: + callback = print_dots(shutdown_event) + + printer('Retrieving speedtest.net configuration...', quiet) + try: + speedtest = Speedtest( + source_address=args.source, + timeout=args.timeout, + secure=args.secure + ) + except (ConfigRetrievalError,) + HTTP_ERRORS: + printer('Cannot retrieve speedtest configuration', error=True) + raise SpeedtestCLIError(get_exception()) + + if args.list: + try: + speedtest.get_servers() + except (ServersRetrievalError,) + HTTP_ERRORS: + printer('Cannot retrieve speedtest server list', error=True) + raise SpeedtestCLIError(get_exception()) + + for _, servers in sorted(speedtest.servers.items()): + for server in servers: + line = ('%(id)5s) %(sponsor)s (%(name)s, %(country)s) ' + '[%(d)0.2f km]' % server) + try: + printer(line) + except IOError: + e = get_exception() + if e.errno != errno.EPIPE: + raise + sys.exit(0) + + printer('Testing from %(isp)s (%(ip)s)...' % speedtest.config['client'], + quiet) + + if not args.mini: + printer('Retrieving speedtest.net server list...', quiet) + try: + speedtest.get_servers(servers=args.server, exclude=args.exclude) + except NoMatchedServers: + raise SpeedtestCLIError( + 'No matched servers: %s' % + ', '.join('%s' % s for s in args.server) + ) + except (ServersRetrievalError,) + HTTP_ERRORS: + printer('Cannot retrieve speedtest server list', error=True) + raise SpeedtestCLIError(get_exception()) + except InvalidServerIDType: + raise SpeedtestCLIError( + '%s is an invalid server type, must ' + 'be an int' % ', '.join('%s' % s for s in args.server) + ) + + if args.server and len(args.server) == 1: + printer('Retrieving information for the selected server...', quiet) + else: + printer('Selecting best server based on ping...', quiet) + speedtest.get_best_server() + elif args.mini: + speedtest.get_best_server(speedtest.set_mini_server(args.mini)) + + results = speedtest.results + + printer('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: ' + '%(latency)s ms' % results.server, quiet) + + if args.download: + printer('Testing download speed', quiet, + end=('', '\n')[bool(debug)]) + speedtest.download( + callback=callback, + threads=(None, 1)[args.single] + ) + printer('Download: %0.2f M%s/s' % + ((results.download / 1000.0 / 1000.0) / args.units[1], + args.units[0]), + quiet) + else: + printer('Skipping download test', quiet) + + if args.upload: + printer('Testing upload speed', quiet, + end=('', '\n')[bool(debug)]) + speedtest.upload( + callback=callback, + pre_allocate=args.pre_allocate, + threads=(None, 1)[args.single] + ) + printer('Upload: %0.2f M%s/s' % + ((results.upload / 1000.0 / 1000.0) / args.units[1], + args.units[0]), + quiet) + else: + printer('Skipping upload test', quiet) + + printer('Results:\n%r' % results.dict(), debug=True) + + if not args.simple and args.share: + results.share() + + if args.simple: + printer('Ping: %s ms\nDownload: %0.2f M%s/s\nUpload: %0.2f M%s/s' % + (results.ping, + (results.download / 1000.0 / 1000.0) / args.units[1], + args.units[0], + (results.upload / 1000.0 / 1000.0) / args.units[1], + args.units[0])) + elif args.csv: + printer(results.csv(delimiter=args.csv_delimiter)) + elif args.json: + printer(results.json()) + + if args.share and not machine_format: + printer('Share results: %s' % results.share()) + + +def main(): + try: + shell() + except KeyboardInterrupt: + printer('\nCancelling...', error=True) + except (SpeedtestException, SystemExit): + e = get_exception() + # Ignore a successful exit, or argparse exit + if getattr(e, 'code', 1) not in (0, 2): + msg = '%s' % e + if not msg: + msg = '%r' % e + raise SystemExit('ERROR: %s' % msg) + + +if __name__ == '__main__': + main() diff --git a/.local/bin/ssh-permissions b/.local/bin/ssh-permissions new file mode 100755 index 0000000..5fb6cde --- /dev/null +++ b/.local/bin/ssh-permissions @@ -0,0 +1,6 @@ +chmod 700 ~/.ssh +chmod 644 ~/.ssh/authorized_keys +chmod 644 ~/.ssh/known_hosts +chmod 644 ~/.ssh/config +chmod 400 ~/.ssh/id_rsa +chmod 400 ~/.ssh/id_rsa.pub diff --git a/.local/bin/stream b/.local/bin/stream new file mode 100755 index 0000000..7662969 --- /dev/null +++ b/.local/bin/stream @@ -0,0 +1,5 @@ +#!/bin/bash +KERNEL=$(uname -sr) + +[[ $KERNEL =~ "icrosoft" ]] && nohup streamlink -p "https://www.twitch.tv/riotgames best" $1 best > /dev/null 2>&1 & +[[ $KERNEL =~ "gentoo" ]] && nohup streamlink -p mpv $1 best > /dev/null 2>&1 & diff --git a/.local/bin/suspend-at-time b/.local/bin/suspend-at-time new file mode 100755 index 0000000..b3ce40a --- /dev/null +++ b/.local/bin/suspend-at-time @@ -0,0 +1,15 @@ +#!/bin/bash +#./suspend-at-time 0:00 + +RUNAT="$1" + +while [ 1 ] +do + DATE=`/bin/date +%H:%M` + if [ $DATE. = $RUNAT. ] + then + sudo systemctl suspend + echo ""DONE" at "$(date)"" + pkill suspend-at-time + fi +done diff --git a/.local/bin/symilar b/.local/bin/symilar new file mode 100755 index 0000000..400edba --- /dev/null +++ b/.local/bin/symilar @@ -0,0 +1,8 @@ +#!/usr/lib/python-exec/python3.7/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_symilar +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_symilar()) diff --git a/.local/bin/tea b/.local/bin/tea new file mode 100755 index 0000000..d5c7f0d Binary files /dev/null and b/.local/bin/tea differ diff --git a/.local/bin/temp b/.local/bin/temp new file mode 100755 index 0000000..95fe391 --- /dev/null +++ b/.local/bin/temp @@ -0,0 +1,3 @@ +#!/bin/bash + +echo -e "CPU: $(sensors | awk '/Core 0/ {print $3}'| egrep --color=no -o '[0-9]+\.' | sed 's/\.//')"; echo -e "GPU: $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')" diff --git a/.local/bin/term-wmi b/.local/bin/term-wmi new file mode 100755 index 0000000..194fb05 --- /dev/null +++ b/.local/bin/term-wmi @@ -0,0 +1,3 @@ +#!/bin/bash +xfreerdp /size:1920:1080 /u:s434812 /d:LABS /v:term.wmi.amu.edu.pl /cert:deny /sound /dynamic-resolution +#rdesktop -u "\LABS\s434812" -g 90% term.wmi.amu.edu.pl diff --git a/.local/bin/trans b/.local/bin/trans new file mode 100755 index 0000000..1d66db2 --- /dev/null +++ b/.local/bin/trans @@ -0,0 +1,5713 @@ +#!/usr/bin/env bash +export TRANS_ENTRY="$0" +if [[ ! $LANG =~ (UTF|utf)-?8$ ]]; then export LANG=en_US.UTF-8; fi +read -r -d '' TRANS_PROGRAM << 'EOF' +BEGIN { +Name = "Translate Shell" +Description = "Command-line translator using Google Translate, Bing Translator, Yandex.Translate, etc." +Version = "0.9.6.12" +ReleaseDate = "2020-05-11" +Command = "trans" +EntryPoint = "translate.awk" +EntryScript = "translate" +} +function initConst() { +NULLSTR = "" +NONE = "\0" +TRUE = 1 +STDIN = "/dev/stdin" +STDOUT = "/dev/stdout" +STDERR = "/dev/stderr" +SUPOUT = " > /dev/null " +SUPERR = " 2> /dev/null " +PIPE = " | " +} +function anything(array, +i) { +for (i in array) +if (array[i]) return 1 +return 0 +} +function exists(value) { +if (isarray(value)) +return anything(value) +else +return value ? 1 : 0 +} +function belongsTo(element, array, +i) { +for (i in array) +if (element == array[i]) return element +return NULLSTR +} +function identical(x, y, +i) { +if (!isarray(x) && !isarray(y)) +return x == y +else if (isarray(x) && isarray(y)) { +if (length(x) != length(y)) return 0 +for (i in x) +if (!identical(x[i], y[i])) return 0 +return 1 +} else +return 0 +} +function append(array, element) { +array[anything(array) ? length(array) : 0] = element +} +function compareByIndexFields(i1, v1, i2, v2, +t1, t2, tl, j) { +split(i1, t1, SUBSEP) +split(i2, t2, SUBSEP) +tl = length(t1) < length(t2) ? length(t1) : length(t2) +for (j = 1; j <= tl; j++) { +if (t1[j] < t2[j]) +return -1 +else if (t1[j] > t2[j]) +return 1 +} +return 0 +} +function isnum(string) { +return string == string + 0 +} +function startsWithAny(string, substrings, +i) { +for (i in substrings) +if (index(string, substrings[i]) == 1) return substrings[i] +return NULLSTR +} +function matchesAny(string, patterns, +i) { +for (i in patterns) +if (string ~ "^" patterns[i]) return patterns[i] +return NULLSTR +} +function replicate(string, len, +i, temp) { +temp = NULLSTR +for (i = 0; i < len; i++) +temp = temp string +return temp +} +function reverse(string, +i, temp) { +temp = NULLSTR +for (i = length(string); i > 0; i--) +temp = temp substr(string, i, 1); +return temp +} +function join(array, separator, sortedIn, preserveNull, +i, j, saveSortedIn, temp) { +if (!sortedIn) +sortedIn = "compareByIndexFields" +temp = NULLSTR +j = 0 +if (isarray(array)) { +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = sortedIn +for (i in array) +if (preserveNull || array[i] != NULLSTR) +temp = j++ ? temp separator array[i] : array[i] +PROCINFO["sorted_in"] = saveSortedIn +} else +temp = array +return temp +} +function explode(string, array) { +split(string, array, NULLSTR) +} +function escapeChar(char) { +switch (char) { +case "b": +return "\b" +case "f": +return "\f" +case "n": +return "\n" +case "r": +return "\r" +case "t": +return "\t" +case "v": +return "\v" +case "u0026": +return "&" +case "u003c": +return "<" +case "u003e": +return ">" +case "u200b": +return "" +default: +return char +} +} +function literal(string, +c, cc, escaping, i, s) { +if (string !~ /^".*"$/) +return string +explode(string, s) +string = NULLSTR +escaping = 0 +for (i = 2; i < length(s); i++) { +c = s[i] +if (escaping) { +if (cc) { +cc = cc c +if (length(cc) == 5) { +string = string escapeChar(cc) +escaping = 0 +cc = NULLSTR +} +} else if (c == "u") { +cc = c +} else { +string = string escapeChar(c) +escaping = 0 +} +} else { +if (c == "\\") +escaping = 1 +else +string = string c +} +} +return string +} +function escape(string) { +gsub(/\\/, "\\\\", string) +gsub(/"/, "\\\"", string) +return string +} +function unescape(string) { +gsub(/\\"/, "\"", string) +gsub(/\\\\/, "\\", string) +return string +} +function parameterize(string, quotationMark) { +if (!quotationMark) +quotationMark = "'" +if (quotationMark == "'") { +gsub(/'/, "'\\''", string) +return "'" string "'" +} else { +return "\"" escape(string) "\"" +} +} +function unparameterize(string, temp) { +match(string, /^'(.*)'$/, temp) +if (temp[0]) { +string = temp[1] +gsub(/'\\''/, "'", string) +return string +} +match(string, /^"(.*)"$/, temp) +if (temp[0]) { +string = temp[1] +return unescape(string) +} +return string +} +function toString(value, inline, heredoc, valOnly, numSub, level, sortedIn, +i, items, j, k, p, saveSortedIn, temp, v) { +if (!level) level = 0 +if (!sortedIn) +sortedIn = "compareByIndexFields" +if (isarray(value)) { +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = sortedIn +p = 0 +for (i in value) { +split(i, j, SUBSEP); k = join(j, ",") +if (!numSub || !isnum(k)) k = parameterize(k, "\"") +v = toString(value[i], inline, heredoc, valOnly, numSub, level + 1, sortedIn) +if (!isarray(value[i])) v = parameterize(v, "\"") +if (valOnly) +items[p++] = inline ? v : (replicate("\t", level) v) +else +items[p++] = inline ? (k ": " v) : +(replicate("\t", level) k "\t" v) +} +PROCINFO["sorted_in"] = saveSortedIn +temp = inline ? join(items, ", ") : +("\n" join(items, "\n") "\n" replicate("\t", level)) +temp = valOnly ? ("[" temp "]") : ("{" temp "}") +return temp +} else { +if (heredoc) +return "'''\n" value "\n'''" +else +return value +} +} +function squeeze(line, preserveIndent) { +if (!preserveIndent) +gsub(/^[[:space:]]+/, NULLSTR, line) +gsub(/^[[:space:]]*#.*$/, NULLSTR, line) +gsub(/#[^"/]*$/, NULLSTR, line) +gsub(/[[:space:]]+$/, NULLSTR, line) +gsub(/[[:space:]]+\\$/, "\\", line) +return line +} +function yn(string) { +return (tolower(string) ~ /^([0fn]|off)/) ? 0 : 1 +} +function initAnsiCode() { +if (ENVIRON["TERM"] == "dumb") return +AnsiCode["reset"] = AnsiCode[0] = "\33[0m" +AnsiCode["bold"] = "\33[1m" +AnsiCode["underline"] = "\33[4m" +AnsiCode["negative"] = "\33[7m" +AnsiCode["no bold"] = "\33[22m" +AnsiCode["no underline"] = "\33[24m" +AnsiCode["positive"] = "\33[27m" +AnsiCode["black"] = "\33[30m" +AnsiCode["red"] = "\33[31m" +AnsiCode["green"] = "\33[32m" +AnsiCode["yellow"] = "\33[33m" +AnsiCode["blue"] = "\33[34m" +AnsiCode["magenta"] = "\33[35m" +AnsiCode["cyan"] = "\33[36m" +AnsiCode["gray"] = "\33[37m" +AnsiCode["default"] = "\33[39m" +AnsiCode["dark gray"] = "\33[90m" +AnsiCode["light red"] = "\33[91m" +AnsiCode["light green"] = "\33[92m" +AnsiCode["light yellow"] = "\33[93m" +AnsiCode["light blue"] = "\33[94m" +AnsiCode["light magenta"] = "\33[95m" +AnsiCode["light cyan"] = "\33[96m" +AnsiCode["white"] = "\33[97m" +} +function ansi(code, text) { +switch (code) { +case "bold": +return AnsiCode[code] text AnsiCode["no bold"] +case "underline": +return AnsiCode[code] text AnsiCode["no underline"] +case "negative": +return AnsiCode[code] text AnsiCode["positive"] +default: +return AnsiCode[code] text AnsiCode[0] +} +} +function w(text) { +print ansi("yellow", text) > STDERR +} +function e(text) { +print ansi("bold", ansi("yellow", text)) > STDERR +} +function wtf(text) { +print ansi("bold", ansi("red", text)) > STDERR +} +function d(text) { +print ansi("gray", text) > STDERR +} +function da(value, name, inline, heredoc, valOnly, numSub, sortedIn, +i, j, saveSortedIn) { +if (!name) +name = "_" +if (!sortedIn) +sortedIn = "compareByIndexFields" +d(name " = " toString(value, inline, heredoc, valOnly, numSub, 0, sortedIn)) +} +function assert(x, message) { +if (!message) +message = "[ERROR] Assertion failed." +if (x) +return x +else +e(message) +} +function initUrlEncoding() { +UrlEncoding["\t"] = "%09" +UrlEncoding["\n"] = "%0A" +UrlEncoding[" "] = "%20" +UrlEncoding["!"] = "%21" +UrlEncoding["\""] = "%22" +UrlEncoding["#"] = "%23" +UrlEncoding["$"] = "%24" +UrlEncoding["%"] = "%25" +UrlEncoding["&"] = "%26" +UrlEncoding["'"] = "%27" +UrlEncoding["("] = "%28" +UrlEncoding[")"] = "%29" +UrlEncoding["*"] = "%2A" +UrlEncoding["+"] = "%2B" +UrlEncoding[","] = "%2C" +UrlEncoding["-"] = "%2D" +UrlEncoding["."] = "%2E" +UrlEncoding["/"] = "%2F" +UrlEncoding[":"] = "%3A" +UrlEncoding[";"] = "%3B" +UrlEncoding["<"] = "%3C" +UrlEncoding["="] = "%3D" +UrlEncoding[">"] = "%3E" +UrlEncoding["?"] = "%3F" +UrlEncoding["@"] = "%40" +UrlEncoding["["] = "%5B" +UrlEncoding["\\"] = "%5C" +UrlEncoding["]"] = "%5D" +UrlEncoding["^"] = "%5E" +UrlEncoding["_"] = "%5F" +UrlEncoding["`"] = "%60" +UrlEncoding["{"] = "%7B" +UrlEncoding["|"] = "%7C" +UrlEncoding["}"] = "%7D" +UrlEncoding["~"] = "%7E" +} +function quote(string, i, r, s) { +r = NULLSTR +explode(string, s) +for (i = 1; i <= length(s); i++) +r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i]) +return r +} +function unquote(string, i, k, r, s, temp) { +r = NULLSTR +explode(string, s) +temp = NULLSTR +for (i = 1; i <= length(s); i++) +if (temp) { +temp = temp s[i] +if (length(temp) > 2) { +for (k in UrlEncoding) +if (temp == UrlEncoding[k]) { +r = r k +temp = NULLSTR +break +} +if (temp) { +r = r temp +temp = NULLSTR +} +} +} else { +if (s[i] != "%") +r = r s[i] +else +temp = s[i] +} +if (temp) +r = r temp +return r +} +function initUriSchemes() { +UriSchemes[0] = "file://" +UriSchemes[1] = "http://" +UriSchemes[2] = "https://" +} +function readFrom(file, line, text) { +if (!file) file = "/dev/stdin" +text = NULLSTR +while (getline line < file) +text = (text ? text "\n" : NULLSTR) line +return text +} +function writeTo(text, file) { +if (!file) file = "/dev/stdout" +print text > file +} +function getOutput(command, content, line) { +content = NULLSTR +while ((command |& getline line) > 0) +content = (content ? content "\n" : NULLSTR) line +close(command) +return content +} +function fileExists(file) { +return !system("test -f " parameterize(file)) +} +function dirExists(file) { +return !system("test -d " parameterize(file)) +} +function detectProgram(prog, arg, returnOutput, command, temp) { +command = prog " " arg SUPERR +command | getline temp +close(command) +if (returnOutput) +return temp +if (temp) +return prog +return NULLSTR +} +function getGitHead( line, group) { +if (fileExists(".git/HEAD")) { +getline line < ".git/HEAD" +match(line, /^ref: (.*)$/, group) +if (fileExists(".git/" group[1])) { +getline line < (".git/" group[1]) +return substr(line, 1, 7) +} else +return NULLSTR +} else +return NULLSTR +} +BEGIN { +initConst() +initAnsiCode() +initUrlEncoding() +initUriSchemes() +} +function initGawk( group) { +Gawk = "gawk" +GawkVersion = PROCINFO["version"] +split(PROCINFO["version"], group, ".") +if (group[1] < 4) { +e("[ERROR] Oops! Your gawk (version " GawkVersion ") "\ +"appears to be too old.\n"\ +" You need at least gawk 4.0.0 to run this program.") +exit 1 +} +} +function initBiDiTerm() { +if (ENVIRON["MLTERM"]) +BiDiTerm = "mlterm" +else if (ENVIRON["KONSOLE_VERSION"]) +BiDiTerm = "konsole" +} +function initBiDi() { +FriBidi = detectProgram("fribidi", "--version", 1) +BiDiNoPad = FriBidi ? "fribidi --nopad" : "rev" SUPERR +BiDi = FriBidi ? "fribidi --width %s" : +"rev" SUPERR "| sed \"s/'/\\\\\\'/\" | xargs -0 printf '%%%ss'" +} +function initRlwrap() { +Rlwrap = detectProgram("rlwrap", "--version") +} +function initEmacs() { +Emacs = detectProgram("emacs", "--version") +} +function initCurl() { +Curl = detectProgram("curl", "--version") +} +function l(value, name, inline, heredoc, valOnly, numSub, sortedIn) { +if (Option["debug"]) { +if (name) +da(value, name, inline, heredoc, valOnly, numSub, sortedIn) +else +d(value) +} +} +function m(string) { +if (Option["debug"]) +return ansi("cyan", string) RS +} +function newerVersion(ver1, ver2, i, group1, group2) { +split(ver1, group1, ".") +split(ver2, group2, ".") +for (i = 1; i <= 4; i++) { +if (group1[i] + 0 > group2[i] + 0) +return 1 +else if (group1[i] + 0 < group2[i] + 0) +return 0 +} +return 0 +} +function rlwrapMe( i, command) { +initRlwrap() +if (!Rlwrap) { +l(">> not found: rlwrap") +return 1 +} +if (ENVIRON["TRANS_ENTRY"]) { +command = Rlwrap " " ENVIRON["TRANS_ENTRY"] " "\ +parameterize("-no-rlwrap") +} else if (fileExists(ENVIRON["TRANS_DIR"] "/" EntryScript)) { +command = Rlwrap " sh "\ +parameterize(ENVIRON["TRANS_DIR"] "/" EntryScript)\ +" - " parameterize("-no-rlwrap") +} else { +l(">> not found: $TRANS_ENTRY or EntryPoint") +return 1 +} +for (i = 1; i < length(ARGV); i++) +if (ARGV[i]) +command = command " " parameterize(ARGV[i]) +l(">> forking: " command) +if (!system(command)) { +l(">> process exited with code 0") +exit ExitCode +} else { +l(">> process exited with non-zero return code") +return 1 +} +} +function emacsMe( i, params, el, command) { +initEmacs() +if (!Emacs) { +l(">> not found: emacs") +return 1 +} +params = "" +for (i = 1; i < length(ARGV); i++) +if (ARGV[i]) +params = params " " parameterize(ARGV[i], "\"") +if (ENVIRON["TRANS_ENTRY"]) { +el = "(progn (setq explicit-shell-file-name \"" ENVIRON["TRANS_ENTRY"] "\") "\ +"(setq explicit-" Command "-args '(\"-I\" \"-no-rlwrap\"" params ")) "\ +"(command-execute 'shell) (rename-buffer \"" Name "\"))" +} else if (fileExists(ENVIRON["TRANS_DIR"] "/" EntryScript)) { +el = "(progn (setq explicit-shell-file-name \"" "sh" "\") "\ +"(setq explicit-" "sh" "-args '(\"" ENVIRON["TRANS_DIR"] "/" EntryScript "\" \"-I\" \"-no-rlwrap\"" params ")) "\ +"(command-execute 'shell) (rename-buffer \"" Name "\"))" +} else { +l(">> not found: $TRANS_ENTRY or EntryPoint") +return 1 +} +command = Emacs " --eval " parameterize(el) +l(">> forking: " command) +if (!system(command)) { +l(">> process exited with code 0") +exit ExitCode +} else { +l(">> process exited with non-zero return code") +return 1 +} +} +function curl(url, output, command, content, line) { +initCurl() +if (!Curl) { +l(">> not found: curl") +w("[WARNING] curl is not found.") +return NULLSTR +} +command = Curl " --location --silent" +if (Option["proxy"]) +command = command " --proxy " parameterize(Option["proxy"]) +if (Option["user-agent"]) +command = command " --user-agent " parameterize(Option["user-agent"]) +command = command " " parameterize(url) +if (output) { +command = command " --output " parameterize(output) +system(command) +return NULLSTR +} +content = NULLSTR +while ((command |& getline line) > 0) +content = (content ? content "\n" : NULLSTR) line +close(command) +return content +} +function curlPost(url, data, output, command, content, line) { +initCurl() +if (!Curl) { +l(">> not found: curl") +w("[WARNING] curl is not found.") +return NULLSTR +} +command = Curl " --location --silent" +if (Option["proxy"]) +command = command " --proxy " parameterize(Option["proxy"]) +if (Option["user-agent"]) +command = command " --user-agent " parameterize(Option["user-agent"]) +command = command " --request POST --data " parameterize(data) +command = command " " parameterize(url) +if (output) { +command = command " --output " parameterize(output) +system(command) +return NULLSTR +} +content = NULLSTR +while ((command |& getline line) > 0) +content = (content ? content "\n" : NULLSTR) line +close(command) +return content +} +function dump(text, group, command, temp) { +command = "hexdump" " -v -e'1/1 \"%03u\" \" \"'" +command = "echo " parameterize(text) PIPE command +command | getline temp +split(temp, group, " ") +close(command) +return length(group) - 1 +} +function dumpX(text, group, command, temp) { +command = "hexdump" " -v -e'1/1 \"%02X\" \" \"'" +command = "echo " parameterize(text) PIPE command +command | getline temp +split(temp, group, " ") +close(command) +return length(group) - 1 +} +function base64(text, command, temp) { +if (detectProgram("uname", "-s", 1) == "Linux") +command = "echo -n " parameterize(text) PIPE "base64 -w0" +else +command = "echo -n " parameterize(text) PIPE "base64" +command = "bash -c " parameterize(command, "\"") +command | getline temp +close(command) +return temp +} +function uprintf(text, command, temp) { +command = "echo -en " parameterize(text) +command = "bash -c " parameterize(command, "\"") +command | getline temp +close(command) +return temp +} +function initLocale() { +Locale["af"]["name"] = "Afrikaans" +Locale["af"]["endonym"] = "Afrikaans" +Locale["af"]["translations-of"] = "Vertalings van %s" +Locale["af"]["definitions-of"] = "Definisies van %s" +Locale["af"]["synonyms"] = "Sinonieme" +Locale["af"]["examples"] = "Voorbeelde" +Locale["af"]["see-also"] = "Sien ook" +Locale["af"]["family"] = "Indo-European" +Locale["af"]["iso"] = "afr" +Locale["af"]["glotto"] = "afri1274" +Locale["af"]["script"] = "Latn" +Locale["sq"]["name"] = "Albanian" +Locale["sq"]["endonym"] = "Shqip" +Locale["sq"]["translations-of"] = "Përkthimet e %s" +Locale["sq"]["definitions-of"] = "Përkufizime të %s" +Locale["sq"]["synonyms"] = "Sinonime" +Locale["sq"]["examples"] = "Shembuj" +Locale["sq"]["see-also"] = "Shihni gjithashtu" +Locale["sq"]["family"] = "Indo-European" +Locale["sq"]["iso"] = "sqi" +Locale["sq"]["glotto"] = "alba1267" +Locale["sq"]["script"] = "Latn" +Locale["am"]["name"] = "Amharic" +Locale["am"]["endonym"] = "አማርኛ" +Locale["am"]["translations-of"] = "የ %s ትርጉሞች" +Locale["am"]["definitions-of"] = "የ %s ቃላት ፍችዎች" +Locale["am"]["synonyms"] = "ተመሳሳይ ቃላት" +Locale["am"]["examples"] = "ምሳሌዎች" +Locale["am"]["see-also"] = "የሚከተለውንም ይመልከቱ" +Locale["am"]["family"] = "Afro-Asiatic" +Locale["am"]["iso"] = "amh" +Locale["am"]["glotto"] = "amha1245" +Locale["am"]["script"] = "Ethi" +Locale["ar"]["name"] = "Arabic" +Locale["ar"]["endonym"] = "العربية" +Locale["ar"]["translations-of"] = "ترجمات %s" +Locale["ar"]["definitions-of"] = "تعريفات %s" +Locale["ar"]["synonyms"] = "مرادفات" +Locale["ar"]["examples"] = "أمثلة" +Locale["ar"]["see-also"] = "انظر أيضًا" +Locale["ar"]["family"] = "Afro-Asiatic" +Locale["ar"]["iso"] = "ara" +Locale["ar"]["glotto"] = "stan1318" +Locale["ar"]["script"] = "Arab" +Locale["ar"]["rtl"] = "true" +Locale["hy"]["name"] = "Armenian" +Locale["hy"]["endonym"] = "Հայերեն" +Locale["hy"]["translations-of"] = "%s-ի թարգմանությունները" +Locale["hy"]["definitions-of"] = "%s-ի սահմանումները" +Locale["hy"]["synonyms"] = "Հոմանիշներ" +Locale["hy"]["examples"] = "Օրինակներ" +Locale["hy"]["see-also"] = "Տես նաև" +Locale["hy"]["family"] = "Indo-European" +Locale["hy"]["iso"] = "hye" +Locale["hy"]["glotto"] = "nucl1235" +Locale["hy"]["script"] = "Armn" +Locale["az"]["name"] = "Azerbaijani" +Locale["az"]["endonym"] = "Azərbaycanca" +Locale["az"]["translations-of"] = "%s sözünün tərcüməsi" +Locale["az"]["definitions-of"] = "%s sözünün tərifləri" +Locale["az"]["synonyms"] = "Sinonimlər" +Locale["az"]["examples"] = "Nümunələr" +Locale["az"]["see-also"] = "Həmçinin, baxın:" +Locale["az"]["family"] = "Turkic" +Locale["az"]["iso"] = "aze" +Locale["az"]["glotto"] = "nort2697" +Locale["az"]["script"] = "Latn" +Locale["eu"]["name"] = "Basque" +Locale["eu"]["endonym"] = "Euskara" +Locale["eu"]["translations-of"] = "%s esapidearen itzulpena" +Locale["eu"]["definitions-of"] = "Honen definizioak: %s" +Locale["eu"]["synonyms"] = "Sinonimoak" +Locale["eu"]["examples"] = "Adibideak" +Locale["eu"]["see-also"] = "Ikusi hauek ere" +Locale["eu"]["family"] = "Language Isolate" +Locale["eu"]["iso"] = "eus" +Locale["eu"]["glotto"] = "basq1248" +Locale["eu"]["script"] = "Latn" +Locale["be"]["name"] = "Belarusian" +Locale["be"]["endonym"] = "беларуская" +Locale["be"]["translations-of"] = "Пераклады %s" +Locale["be"]["definitions-of"] = "Вызначэннi %s" +Locale["be"]["synonyms"] = "Сінонімы" +Locale["be"]["examples"] = "Прыклады" +Locale["be"]["see-also"] = "Гл. таксама" +Locale["be"]["family"] = "Indo-European" +Locale["be"]["iso"] = "bel" +Locale["be"]["glotto"] = "bela1254" +Locale["be"]["script"] = "Cyrl" +Locale["bn"]["name"] = "Bengali" +Locale["bn"]["endonym"] = "বাংলা" +Locale["bn"]["translations-of"] = "%s এর অনুবাদ" +Locale["bn"]["definitions-of"] = "%s এর সংজ্ঞা" +Locale["bn"]["synonyms"] = "প্রতিশব্দ" +Locale["bn"]["examples"] = "উদাহরণ" +Locale["bn"]["see-also"] = "আরো দেখুন" +Locale["bn"]["family"] = "Indo-European" +Locale["bn"]["iso"] = "ben" +Locale["bn"]["glotto"] = "beng1280" +Locale["bn"]["script"] = "Beng" +Locale["bs"]["name"] = "Bosnian" +Locale["bs"]["endonym"] = "Bosanski" +Locale["bs"]["translations-of"] = "Prijevod za: %s" +Locale["bs"]["definitions-of"] = "Definicije za %s" +Locale["bs"]["synonyms"] = "Sinonimi" +Locale["bs"]["examples"] = "Primjeri" +Locale["bs"]["see-also"] = "Pogledajte i" +Locale["bs"]["family"] = "Indo-European" +Locale["bs"]["iso"] = "bos" +Locale["bs"]["glotto"] = "bosn1245" +Locale["bs"]["script"] = "Latn" +Locale["bg"]["name"] = "Bulgarian" +Locale["bg"]["endonym"] = "български" +Locale["bg"]["translations-of"] = "Преводи на %s" +Locale["bg"]["definitions-of"] = "Дефиниции за %s" +Locale["bg"]["synonyms"] = "Синоними" +Locale["bg"]["examples"] = "Примери" +Locale["bg"]["see-also"] = "Вижте също" +Locale["bg"]["family"] = "Indo-European" +Locale["bg"]["iso"] = "bul" +Locale["bg"]["glotto"] = "bulg1262" +Locale["bg"]["script"] = "Cyrl" +Locale["ca"]["name"] = "Catalan" +Locale["ca"]["endonym"] = "Català" +Locale["ca"]["translations-of"] = "Traduccions per a %s" +Locale["ca"]["definitions-of"] = "Definicions de: %s" +Locale["ca"]["synonyms"] = "Sinònims" +Locale["ca"]["examples"] = "Exemples" +Locale["ca"]["see-also"] = "Vegeu també" +Locale["ca"]["family"] = "Indo-European" +Locale["ca"]["iso"] = "cat" +Locale["ca"]["glotto"] = "stan1289" +Locale["ca"]["script"] = "Latn" +Locale["ceb"]["name"] = "Cebuano" +Locale["ceb"]["endonym"] = "Cebuano" +Locale["ceb"]["translations-of"] = "%s Mga Paghubad sa PULONG_O_HUGPONG SA PAMULONG" +Locale["ceb"]["definitions-of"] = "Mga kahulugan sa %s" +Locale["ceb"]["synonyms"] = "Mga Kapulong" +Locale["ceb"]["examples"] = "Mga pananglitan:" +Locale["ceb"]["see-also"] = "Kitaa pag-usab" +Locale["ceb"]["family"] = "Austronesian" +Locale["ceb"]["iso"] = "ceb" +Locale["ceb"]["glotto"] = "cebu1242" +Locale["ceb"]["script"] = "Latn" +Locale["ny"]["name"] = "Chichewa" +Locale["ny"]["endonym"] = "Nyanja" +Locale["ny"]["translations-of"] = "Matanthauzidwe a %s" +Locale["ny"]["definitions-of"] = "Mamasulidwe a %s" +Locale["ny"]["synonyms"] = "Mau ofanana" +Locale["ny"]["examples"] = "Zitsanzo" +Locale["ny"]["see-also"] = "Onaninso" +Locale["ny"]["family"] = "Atlantic-Congo" +Locale["ny"]["iso"] = "nya" +Locale["ny"]["glotto"] = "nyan1308" +Locale["ny"]["script"] = "Latn" +Locale["zh-CN"]["name"] = "Chinese Simplified" +Locale["zh-CN"]["endonym"] = "简体中文" +Locale["zh-CN"]["translations-of"] = "%s 的翻译" +Locale["zh-CN"]["definitions-of"] = "%s的定义" +Locale["zh-CN"]["synonyms"] = "同义词" +Locale["zh-CN"]["examples"] = "示例" +Locale["zh-CN"]["see-also"] = "另请参阅" +Locale["zh-CN"]["family"] = "Sino-Tibetan" +Locale["zh-CN"]["iso"] = "zho-CN" +Locale["zh-CN"]["glotto"] = "mand1415" +Locale["zh-CN"]["script"] = "Hans" +Locale["zh-CN"]["dictionary"] = "true" +Locale["zh-TW"]["name"] = "Chinese Traditional" +Locale["zh-TW"]["endonym"] = "正體中文" +Locale["zh-TW"]["translations-of"] = "「%s」的翻譯" +Locale["zh-TW"]["definitions-of"] = "「%s」的定義" +Locale["zh-TW"]["synonyms"] = "同義詞" +Locale["zh-TW"]["examples"] = "例句" +Locale["zh-TW"]["see-also"] = "另請參閱" +Locale["zh-TW"]["family"] = "Sino-Tibetan" +Locale["zh-TW"]["iso"] = "zho-TW" +Locale["zh-TW"]["glotto"] = "mand1415" +Locale["zh-TW"]["script"] = "Hant" +Locale["zh-TW"]["dictionary"] = "true" +Locale["co"]["name"] = "Corsican" +Locale["co"]["endonym"] = "Corsu" +Locale["co"]["translations-of"] = "Traductions de %s" +Locale["co"]["definitions-of"] = "Définitions de %s" +Locale["co"]["synonyms"] = "Synonymes" +Locale["co"]["examples"] = "Exemples" +Locale["co"]["see-also"] = "Voir aussi" +Locale["co"]["family"] = "Indo-European" +Locale["co"]["iso"] = "cos" +Locale["co"]["glotto"] = "cors1242" +Locale["co"]["script"] = "Latn" +Locale["hr"]["name"] = "Croatian" +Locale["hr"]["endonym"] = "Hrvatski" +Locale["hr"]["translations-of"] = "Prijevodi riječi ili izraza %s" +Locale["hr"]["definitions-of"] = "Definicije riječi ili izraza %s" +Locale["hr"]["synonyms"] = "Sinonimi" +Locale["hr"]["examples"] = "Primjeri" +Locale["hr"]["see-also"] = "Također pogledajte" +Locale["hr"]["family"] = "Indo-European" +Locale["hr"]["iso"] = "hrv" +Locale["hr"]["glotto"] = "croa1245" +Locale["hr"]["script"] = "Latn" +Locale["cs"]["name"] = "Czech" +Locale["cs"]["endonym"] = "Čeština" +Locale["cs"]["translations-of"] = "Překlad výrazu %s" +Locale["cs"]["definitions-of"] = "Definice výrazu %s" +Locale["cs"]["synonyms"] = "Synonyma" +Locale["cs"]["examples"] = "Příklady" +Locale["cs"]["see-also"] = "Viz také" +Locale["cs"]["family"] = "Indo-European" +Locale["cs"]["iso"] = "ces" +Locale["cs"]["glotto"] = "czec1258" +Locale["cs"]["script"] = "Latn" +Locale["da"]["name"] = "Danish" +Locale["da"]["endonym"] = "Dansk" +Locale["da"]["translations-of"] = "Oversættelser af %s" +Locale["da"]["definitions-of"] = "Definitioner af %s" +Locale["da"]["synonyms"] = "Synonymer" +Locale["da"]["examples"] = "Eksempler" +Locale["da"]["see-also"] = "Se også" +Locale["da"]["family"] = "Indo-European" +Locale["da"]["iso"] = "dan" +Locale["da"]["glotto"] = "dani1285" +Locale["da"]["script"] = "Latn" +Locale["nl"]["name"] = "Dutch" +Locale["nl"]["endonym"] = "Nederlands" +Locale["nl"]["translations-of"] = "Vertalingen van %s" +Locale["nl"]["definitions-of"] = "Definities van %s" +Locale["nl"]["synonyms"] = "Synoniemen" +Locale["nl"]["examples"] = "Voorbeelden" +Locale["nl"]["see-also"] = "Zie ook" +Locale["nl"]["family"] = "Indo-European" +Locale["nl"]["iso"] = "nld" +Locale["nl"]["glotto"] = "dutc1256" +Locale["nl"]["script"] = "Latn" +Locale["nl"]["dictionary"] = "true" +Locale["en"]["name"] = "English" +Locale["en"]["endonym"] = "English" +Locale["en"]["translations-of"] = "Translations of %s" +Locale["en"]["definitions-of"] = "Definitions of %s" +Locale["en"]["synonyms"] = "Synonyms" +Locale["en"]["examples"] = "Examples" +Locale["en"]["see-also"] = "See also" +Locale["en"]["family"] = "Indo-European" +Locale["en"]["iso"] = "eng" +Locale["en"]["glotto"] = "stan1293" +Locale["en"]["script"] = "Latn" +Locale["en"]["dictionary"] = "true" +Locale["eo"]["name"] = "Esperanto" +Locale["eo"]["endonym"] = "Esperanto" +Locale["eo"]["translations-of"] = "Tradukoj de %s" +Locale["eo"]["definitions-of"] = "Difinoj de %s" +Locale["eo"]["synonyms"] = "Sinonimoj" +Locale["eo"]["examples"] = "Ekzemploj" +Locale["eo"]["see-also"] = "Vidu ankaŭ" +Locale["eo"]["family"] = "Artificial Language" +Locale["eo"]["iso"] = "epo" +Locale["eo"]["glotto"] = "espe1235" +Locale["eo"]["script"] = "Latn" +Locale["et"]["name"] = "Estonian" +Locale["et"]["endonym"] = "Eesti" +Locale["et"]["translations-of"] = "Sõna(de) %s tõlked" +Locale["et"]["definitions-of"] = "Sõna(de) %s definitsioonid" +Locale["et"]["synonyms"] = "Sünonüümid" +Locale["et"]["examples"] = "Näited" +Locale["et"]["see-also"] = "Vt ka" +Locale["et"]["family"] = "Uralic" +Locale["et"]["iso"] = "est" +Locale["et"]["glotto"] = "esto1258" +Locale["et"]["script"] = "Latn" +Locale["tl"]["name"] = "Filipino" +Locale["tl"]["endonym"] = "Tagalog" +Locale["tl"]["translations-of"] = "Mga pagsasalin ng %s" +Locale["tl"]["definitions-of"] = "Mga kahulugan ng %s" +Locale["tl"]["synonyms"] = "Mga Kasingkahulugan" +Locale["tl"]["examples"] = "Mga Halimbawa" +Locale["tl"]["see-also"] = "Tingnan rin ang" +Locale["tl"]["family"] = "Austronesian" +Locale["tl"]["iso"] = "tgl" +Locale["tl"]["glotto"] = "taga1270" +Locale["tl"]["script"] = "Latn" +Locale["fi"]["name"] = "Finnish" +Locale["fi"]["endonym"] = "Suomi" +Locale["fi"]["translations-of"] = "Käännökset tekstille %s" +Locale["fi"]["definitions-of"] = "Määritelmät kohteelle %s" +Locale["fi"]["synonyms"] = "Synonyymit" +Locale["fi"]["examples"] = "Esimerkkejä" +Locale["fi"]["see-also"] = "Katso myös" +Locale["fi"]["family"] = "Uralic" +Locale["fi"]["iso"] = "fin" +Locale["fi"]["glotto"] = "finn1318" +Locale["fi"]["script"] = "Latn" +Locale["fr"]["name"] = "French" +Locale["fr"]["endonym"] = "Français" +Locale["fr"]["translations-of"] = "Traductions de %s" +Locale["fr"]["definitions-of"] = "Définitions de %s" +Locale["fr"]["synonyms"] = "Synonymes" +Locale["fr"]["examples"] = "Exemples" +Locale["fr"]["see-also"] = "Voir aussi" +Locale["fr"]["family"] = "Indo-European" +Locale["fr"]["iso"] = "fra" +Locale["fr"]["glotto"] = "stan1290" +Locale["fr"]["script"] = "Latn" +Locale["fr"]["dictionary"] = "true" +Locale["gl"]["name"] = "Galician" +Locale["gl"]["endonym"] = "Galego" +Locale["gl"]["translations-of"] = "Traducións de %s" +Locale["gl"]["definitions-of"] = "Definicións de %s" +Locale["gl"]["synonyms"] = "Sinónimos" +Locale["gl"]["examples"] = "Exemplos" +Locale["gl"]["see-also"] = "Ver tamén" +Locale["gl"]["family"] = "Indo-European" +Locale["gl"]["iso"] = "glg" +Locale["gl"]["glotto"] = "gali1258" +Locale["gl"]["script"] = "Latn" +Locale["ka"]["name"] = "Georgian" +Locale["ka"]["endonym"] = "ქართული" +Locale["ka"]["translations-of"] = "%s-ის თარგმანები" +Locale["ka"]["definitions-of"] = "%s-ის განსაზღვრებები" +Locale["ka"]["synonyms"] = "სინონიმები" +Locale["ka"]["examples"] = "მაგალითები" +Locale["ka"]["see-also"] = "ასევე იხილეთ" +Locale["ka"]["family"] = "Kartvelian" +Locale["ka"]["iso"] = "kat" +Locale["ka"]["glotto"] = "nucl1302" +Locale["ka"]["script"] = "Geor" +Locale["de"]["name"] = "German" +Locale["de"]["endonym"] = "Deutsch" +Locale["de"]["translations-of"] = "Übersetzungen für %s" +Locale["de"]["definitions-of"] = "Definitionen von %s" +Locale["de"]["synonyms"] = "Synonyme" +Locale["de"]["examples"] = "Beispiele" +Locale["de"]["see-also"] = "Siehe auch" +Locale["de"]["family"] = "Indo-European" +Locale["de"]["iso"] = "deu" +Locale["de"]["glotto"] = "stan1295" +Locale["de"]["script"] = "Latn" +Locale["de"]["dictionary"] = "true" +Locale["el"]["name"] = "Greek" +Locale["el"]["endonym"] = "Ελληνικά" +Locale["el"]["translations-of"] = "Μεταφράσεις του %s" +Locale["el"]["definitions-of"] = "Όρισμοί %s" +Locale["el"]["synonyms"] = "Συνώνυμα" +Locale["el"]["examples"] = "Παραδείγματα" +Locale["el"]["see-also"] = "Δείτε επίσης" +Locale["el"]["family"] = "Indo-European" +Locale["el"]["iso"] = "ell" +Locale["el"]["glotto"] = "mode1248" +Locale["el"]["script"] = "Grek" +Locale["gu"]["name"] = "Gujarati" +Locale["gu"]["endonym"] = "ગુજરાતી" +Locale["gu"]["translations-of"] = "%s ના અનુવાદ" +Locale["gu"]["definitions-of"] = "%s ની વ્યાખ્યાઓ" +Locale["gu"]["synonyms"] = "સમાનાર્થી" +Locale["gu"]["examples"] = "ઉદાહરણો" +Locale["gu"]["see-also"] = "આ પણ જુઓ" +Locale["gu"]["family"] = "Indo-European" +Locale["gu"]["iso"] = "guj" +Locale["gu"]["glotto"] = "guja1252" +Locale["gu"]["script"] = "Gujr" +Locale["ht"]["name"] = "Haitian Creole" +Locale["ht"]["endonym"] = "Kreyòl Ayisyen" +Locale["ht"]["translations-of"] = "Tradiksyon %s" +Locale["ht"]["definitions-of"] = "Definisyon nan %s" +Locale["ht"]["synonyms"] = "Sinonim" +Locale["ht"]["examples"] = "Egzanp:" +Locale["ht"]["see-also"] = "Wè tou" +Locale["ht"]["family"] = "Indo-European" +Locale["ht"]["iso"] = "hat" +Locale["ht"]["glotto"] = "hait1244" +Locale["ht"]["script"] = "Latn" +Locale["haw"]["name"] = "Hawaiian" +Locale["haw"]["endonym"] = "ʻŌlelo Hawaiʻi" +Locale["haw"]["family"] = "Austronesian" +Locale["haw"]["iso"] = "haw" +Locale["haw"]["glotto"] = "hawa1245" +Locale["haw"]["script"] = "Latn" +Locale["ha"]["name"] = "Hausa" +Locale["ha"]["endonym"] = "Hausa" +Locale["ha"]["translations-of"] = "Fassarar %s" +Locale["ha"]["definitions-of"] = "Ma'anoni na %s" +Locale["ha"]["synonyms"] = "Masu kamancin ma'ana" +Locale["ha"]["examples"] = "Misalai" +Locale["ha"]["see-also"] = "Duba kuma" +Locale["ha"]["family"] = "Afro-Asiatic" +Locale["ha"]["iso"] = "hau" +Locale["ha"]["glotto"] = "haus1257" +Locale["ha"]["script"] = "Latn" +Locale["he"]["name"] = "Hebrew" +Locale["he"]["endonym"] = "עִבְרִית" +Locale["he"]["translations-of"] = "תרגומים של %s" +Locale["he"]["definitions-of"] = "הגדרות של %s" +Locale["he"]["synonyms"] = "מילים נרדפות" +Locale["he"]["examples"] = "דוגמאות" +Locale["he"]["see-also"] = "ראה גם" +Locale["he"]["family"] = "Afro-Asiatic" +Locale["he"]["iso"] = "heb" +Locale["he"]["glotto"] = "hebr1245" +Locale["he"]["script"] = "Hebr" +Locale["he"]["rtl"] = "true" +Locale["hi"]["name"] = "Hindi" +Locale["hi"]["endonym"] = "हिन्दी" +Locale["hi"]["translations-of"] = "%s के अनुवाद" +Locale["hi"]["definitions-of"] = "%s की परिभाषाएं" +Locale["hi"]["synonyms"] = "समानार्थी" +Locale["hi"]["examples"] = "उदाहरण" +Locale["hi"]["see-also"] = "यह भी देखें" +Locale["hi"]["family"] = "Indo-European" +Locale["hi"]["iso"] = "hin" +Locale["hi"]["glotto"] = "hind1269" +Locale["hi"]["script"] = "Deva" +Locale["hmn"]["name"] = "Hmong" +Locale["hmn"]["endonym"] = "Hmoob" +Locale["hmn"]["translations-of"] = "Lus txhais: %s" +Locale["hmn"]["family"] = "Hmong-Mien" +Locale["hmn"]["iso"] = "hmn" +Locale["hmn"]["glotto"] = "firs1234" +Locale["hmn"]["script"] = "Latn" +Locale["hu"]["name"] = "Hungarian" +Locale["hu"]["endonym"] = "Magyar" +Locale["hu"]["translations-of"] = "%s fordításai" +Locale["hu"]["definitions-of"] = "%s jelentései" +Locale["hu"]["synonyms"] = "Szinonimák" +Locale["hu"]["examples"] = "Példák" +Locale["hu"]["see-also"] = "Lásd még" +Locale["hu"]["family"] = "Uralic" +Locale["hu"]["iso"] = "hun" +Locale["hu"]["glotto"] = "hung1274" +Locale["hu"]["script"] = "Latn" +Locale["is"]["name"] = "Icelandic" +Locale["is"]["endonym"] = "Íslenska" +Locale["is"]["translations-of"] = "Þýðingar á %s" +Locale["is"]["definitions-of"] = "Skilgreiningar á" +Locale["is"]["synonyms"] = "Samheiti" +Locale["is"]["examples"] = "Dæmi" +Locale["is"]["see-also"] = "Sjá einnig" +Locale["is"]["family"] = "Indo-European" +Locale["is"]["iso"] = "isl" +Locale["is"]["glotto"] = "icel1247" +Locale["is"]["script"] = "Latn" +Locale["ig"]["name"] = "Igbo" +Locale["ig"]["endonym"] = "Igbo" +Locale["ig"]["translations-of"] = "Ntụgharị asụsụ nke %s" +Locale["ig"]["definitions-of"] = "Nkọwapụta nke %s" +Locale["ig"]["synonyms"] = "Okwu oyiri" +Locale["ig"]["examples"] = "Ọmụmaatụ" +Locale["ig"]["see-also"] = "Hụkwuo" +Locale["ig"]["family"] = "Atlantic-Congo" +Locale["ig"]["iso"] = "ibo" +Locale["ig"]["glotto"] = "nucl1417" +Locale["ig"]["script"] = "Latn" +Locale["id"]["name"] = "Indonesian" +Locale["id"]["endonym"] = "Bahasa Indonesia" +Locale["id"]["translations-of"] = "Terjemahan dari %s" +Locale["id"]["definitions-of"] = "Definisi %s" +Locale["id"]["synonyms"] = "Sinonim" +Locale["id"]["examples"] = "Contoh" +Locale["id"]["see-also"] = "Lihat juga" +Locale["id"]["family"] = "Austronesian" +Locale["id"]["iso"] = "ind" +Locale["id"]["glotto"] = "indo1316" +Locale["id"]["script"] = "Latn" +Locale["ga"]["name"] = "Irish" +Locale["ga"]["endonym"] = "Gaeilge" +Locale["ga"]["translations-of"] = "Aistriúcháin ar %s" +Locale["ga"]["definitions-of"] = "Sainmhínithe ar %s" +Locale["ga"]["synonyms"] = "Comhchiallaigh" +Locale["ga"]["examples"] = "Samplaí" +Locale["ga"]["see-also"] = "féach freisin" +Locale["ga"]["family"] = "Indo-European" +Locale["ga"]["iso"] = "gle" +Locale["ga"]["glotto"] = "iris1253" +Locale["ga"]["script"] = "Latn" +Locale["it"]["name"] = "Italian" +Locale["it"]["endonym"] = "Italiano" +Locale["it"]["translations-of"] = "Traduzioni di %s" +Locale["it"]["definitions-of"] = "Definizioni di %s" +Locale["it"]["synonyms"] = "Sinonimi" +Locale["it"]["examples"] = "Esempi" +Locale["it"]["see-also"] = "Vedi anche" +Locale["it"]["family"] = "Indo-European" +Locale["it"]["iso"] = "ita" +Locale["it"]["glotto"] = "ital1282" +Locale["it"]["script"] = "Latn" +Locale["it"]["dictionary"] = "true" +Locale["ja"]["name"] = "Japanese" +Locale["ja"]["endonym"] = "日本語" +Locale["ja"]["translations-of"] = "「%s」の翻訳" +Locale["ja"]["definitions-of"] = "%s の定義" +Locale["ja"]["synonyms"] = "同義語" +Locale["ja"]["examples"] = "例" +Locale["ja"]["see-also"] = "関連項目" +Locale["ja"]["family"] = "Japonic" +Locale["ja"]["iso"] = "jpn" +Locale["ja"]["glotto"] = "nucl1643" +Locale["ja"]["script"] = "Jpan" +Locale["ja"]["dictionary"] = "true" +Locale["jv"]["name"] = "Javanese" +Locale["jv"]["endonym"] = "Basa Jawa" +Locale["jv"]["translations-of"] = "Terjemahan %s" +Locale["jv"]["definitions-of"] = "Arti %s" +Locale["jv"]["synonyms"] = "Sinonim" +Locale["jv"]["examples"] = "Conto" +Locale["jv"]["see-also"] = "Deleng uga" +Locale["jv"]["family"] = "Austronesian" +Locale["jv"]["iso"] = "jav" +Locale["jv"]["glotto"] = "java1254" +Locale["jv"]["script"] = "Latn" +Locale["kn"]["name"] = "Kannada" +Locale["kn"]["endonym"] = "ಕನ್ನಡ" +Locale["kn"]["translations-of"] = "%s ನ ಅನುವಾದಗಳು" +Locale["kn"]["definitions-of"] = "%s ನ ವ್ಯಾಖ್ಯಾನಗಳು" +Locale["kn"]["synonyms"] = "ಸಮಾನಾರ್ಥಕಗಳು" +Locale["kn"]["examples"] = "ಉದಾಹರಣೆಗಳು" +Locale["kn"]["see-also"] = "ಇದನ್ನೂ ಗಮನಿಸಿ" +Locale["kn"]["family"] = "Dravidian" +Locale["kn"]["iso"] = "kan" +Locale["kn"]["glotto"] = "nucl1305" +Locale["kn"]["script"] = "Knda" +Locale["kk"]["name"] = "Kazakh" +Locale["kk"]["endonym"] = "Қазақ тілі" +Locale["kk"]["translations-of"] = "%s аудармалары" +Locale["kk"]["definitions-of"] = "%s анықтамалары" +Locale["kk"]["synonyms"] = "Синонимдер" +Locale["kk"]["examples"] = "Мысалдар" +Locale["kk"]["see-also"] = "Келесі тізімді де көріңіз:" +Locale["kk"]["family"] = "Turkic" +Locale["kk"]["iso"] = "kaz" +Locale["kk"]["glotto"] = "kaza1248" +Locale["kk"]["script"] = "Cyrl" +Locale["km"]["name"] = "Khmer" +Locale["km"]["endonym"] = "ភាសាខ្មែរ" +Locale["km"]["translations-of"] = "ការ​បក​ប្រែ​នៃ %s" +Locale["km"]["definitions-of"] = "និយមន័យ​នៃ​ %s" +Locale["km"]["synonyms"] = "សទិសន័យ" +Locale["km"]["examples"] = "ឧទាហរណ៍" +Locale["km"]["see-also"] = "មើល​ផង​ដែរ" +Locale["km"]["family"] = "Austroasiatic" +Locale["km"]["iso"] = "khm" +Locale["km"]["glotto"] = "cent1989" +Locale["km"]["script"] = "Khmr" +Locale["ko"]["name"] = "Korean" +Locale["ko"]["endonym"] = "한국어" +Locale["ko"]["translations-of"] = "%s의 번역" +Locale["ko"]["definitions-of"] = "%s의 정의" +Locale["ko"]["synonyms"] = "동의어" +Locale["ko"]["examples"] = "예문" +Locale["ko"]["see-also"] = "참조" +Locale["ko"]["family"] = "Koreanic" +Locale["ko"]["iso"] = "kor" +Locale["ko"]["glotto"] = "kore1280" +Locale["ko"]["script"] = "Kore" +Locale["ko"]["dictionary"] = "true" +Locale["ku"]["name"] = "Kurdish" +Locale["ku"]["endonym"] = "Kurdî" +Locale["ku"]["family"] = "Indo-European" +Locale["ku"]["iso"] = "kur" +Locale["ku"]["glotto"] = "kurd1259" +Locale["ku"]["script"] = "Latn" +Locale["ky"]["name"] = "Kyrgyz" +Locale["ky"]["endonym"] = "Кыргызча" +Locale["ky"]["translations-of"] = "%s котормосу" +Locale["ky"]["definitions-of"] = "%s аныктамасы" +Locale["ky"]["synonyms"] = "Синонимдер" +Locale["ky"]["examples"] = "Мисалдар" +Locale["ky"]["see-also"] = "Дагы караңыз" +Locale["ky"]["family"] = "Turkic" +Locale["ky"]["iso"] = "kir" +Locale["ky"]["glotto"] = "kirg1245" +Locale["ky"]["script"] = "Cyrl" +Locale["lo"]["name"] = "Lao" +Locale["lo"]["endonym"] = "ລາວ" +Locale["lo"]["translations-of"] = "ຄຳ​ແປ​ສຳລັບ %s" +Locale["lo"]["definitions-of"] = "ຄວາມໝາຍຂອງ %s" +Locale["lo"]["synonyms"] = "ຄຳທີ່ຄ້າຍກັນ %s" +Locale["lo"]["examples"] = "ຕົວຢ່າງ" +Locale["lo"]["see-also"] = "ເບິ່ງ​ເພີ່ມ​ເຕີມ" +Locale["lo"]["family"] = "Tai-Kadai" +Locale["lo"]["iso"] = "lao" +Locale["lo"]["glotto"] = "laoo1244" +Locale["lo"]["script"] = "Laoo" +Locale["la"]["name"] = "Latin" +Locale["la"]["endonym"] = "Latina" +Locale["la"]["translations-of"] = "Versio de %s" +Locale["la"]["family"] = "Indo-European" +Locale["la"]["iso"] = "lat" +Locale["la"]["glotto"] = "lati1261" +Locale["la"]["script"] = "Latn" +Locale["lv"]["name"] = "Latvian" +Locale["lv"]["endonym"] = "Latviešu" +Locale["lv"]["translations-of"] = "%s tulkojumi" +Locale["lv"]["definitions-of"] = "%s definīcijas" +Locale["lv"]["synonyms"] = "Sinonīmi" +Locale["lv"]["examples"] = "Piemēri" +Locale["lv"]["see-also"] = "Skatiet arī" +Locale["lv"]["family"] = "Indo-European" +Locale["lv"]["iso"] = "lav" +Locale["lv"]["glotto"] = "latv1249" +Locale["lv"]["script"] = "Latn" +Locale["lt"]["name"] = "Lithuanian" +Locale["lt"]["endonym"] = "Lietuvių" +Locale["lt"]["translations-of"] = "„%s“ vertimai" +Locale["lt"]["definitions-of"] = "„%s“ apibrėžimai" +Locale["lt"]["synonyms"] = "Sinonimai" +Locale["lt"]["examples"] = "Pavyzdžiai" +Locale["lt"]["see-also"] = "Taip pat žiūrėkite" +Locale["lt"]["family"] = "Indo-European" +Locale["lt"]["iso"] = "lit" +Locale["lt"]["glotto"] = "lith1251" +Locale["lt"]["script"] = "Latn" +Locale["lb"]["name"] = "Luxembourgish" +Locale["lb"]["endonym"] = "Lëtzebuergesch" +Locale["lb"]["family"] = "Indo-European" +Locale["lb"]["iso"] = "ltz" +Locale["lb"]["glotto"] = "luxe1241" +Locale["lb"]["script"] = "Latn" +Locale["mk"]["name"] = "Macedonian" +Locale["mk"]["endonym"] = "Македонски" +Locale["mk"]["translations-of"] = "Преводи на %s" +Locale["mk"]["definitions-of"] = "Дефиниции на %s" +Locale["mk"]["synonyms"] = "Синоними" +Locale["mk"]["examples"] = "Примери" +Locale["mk"]["see-also"] = "Види и" +Locale["mk"]["family"] = "Indo-European" +Locale["mk"]["iso"] = "mkd" +Locale["mk"]["glotto"] = "mace1250" +Locale["mk"]["script"] = "Cyrl" +Locale["mg"]["name"] = "Malagasy" +Locale["mg"]["endonym"] = "Malagasy" +Locale["mg"]["translations-of"] = "Dikan'ny %s" +Locale["mg"]["definitions-of"] = "Famaritana ny %s" +Locale["mg"]["synonyms"] = "Mitovy hevitra" +Locale["mg"]["examples"] = "Ohatra" +Locale["mg"]["see-also"] = "Jereo ihany koa" +Locale["mg"]["family"] = "Austronesian" +Locale["mg"]["iso"] = "mlg" +Locale["mg"]["glotto"] = "plat1254" +Locale["mg"]["script"] = "Latn" +Locale["ms"]["name"] = "Malay" +Locale["ms"]["endonym"] = "Bahasa Melayu" +Locale["ms"]["translations-of"] = "Terjemahan %s" +Locale["ms"]["definitions-of"] = "Takrif %s" +Locale["ms"]["synonyms"] = "Sinonim" +Locale["ms"]["examples"] = "Contoh" +Locale["ms"]["see-also"] = "Lihat juga" +Locale["ms"]["family"] = "Austronesian" +Locale["ms"]["iso"] = "msa" +Locale["ms"]["glotto"] = "stan1306" +Locale["ms"]["script"] = "Latn" +Locale["ml"]["name"] = "Malayalam" +Locale["ml"]["endonym"] = "മലയാളം" +Locale["ml"]["translations-of"] = "%s എന്നതിന്റെ വിവർത്തനങ്ങൾ" +Locale["ml"]["definitions-of"] = "%s എന്നതിന്റെ നിർവ്വചനങ്ങൾ" +Locale["ml"]["synonyms"] = "പര്യായങ്ങള്‍" +Locale["ml"]["examples"] = "ഉദാഹരണങ്ങള്‍" +Locale["ml"]["see-also"] = "ഇതും കാണുക" +Locale["ml"]["family"] = "Dravidian" +Locale["ml"]["iso"] = "mal" +Locale["ml"]["glotto"] = "mala1464" +Locale["ml"]["script"] = "Mlym" +Locale["mt"]["name"] = "Maltese" +Locale["mt"]["endonym"] = "Malti" +Locale["mt"]["translations-of"] = "Traduzzjonijiet ta' %s" +Locale["mt"]["definitions-of"] = "Definizzjonijiet ta' %s" +Locale["mt"]["synonyms"] = "Sinonimi" +Locale["mt"]["examples"] = "Eżempji" +Locale["mt"]["see-also"] = "Ara wkoll" +Locale["mt"]["family"] = "Afro-Asiatic" +Locale["mt"]["iso"] = "mlt" +Locale["mt"]["glotto"] = "malt1254" +Locale["mt"]["script"] = "Latn" +Locale["mi"]["name"] = "Maori" +Locale["mi"]["endonym"] = "Māori" +Locale["mi"]["translations-of"] = "Ngā whakamāoritanga o %s" +Locale["mi"]["definitions-of"] = "Ngā whakamārama o %s" +Locale["mi"]["synonyms"] = "Ngā Kupu Taurite" +Locale["mi"]["examples"] = "Ngā Tauira:" +Locale["mi"]["see-also"] = "Tiro hoki:" +Locale["mi"]["family"] = "Austronesian" +Locale["mi"]["iso"] = "mri" +Locale["mi"]["glotto"] = "maor1246" +Locale["mi"]["script"] = "Latn" +Locale["mr"]["name"] = "Marathi" +Locale["mr"]["endonym"] = "मराठी" +Locale["mr"]["translations-of"] = "%s ची भाषांतरे" +Locale["mr"]["definitions-of"] = "%s च्या व्याख्या" +Locale["mr"]["synonyms"] = "समानार्थी शब्द" +Locale["mr"]["examples"] = "उदाहरणे" +Locale["mr"]["see-also"] = "हे देखील पहा" +Locale["mr"]["family"] = "Indo-European" +Locale["mr"]["iso"] = "mar" +Locale["mr"]["glotto"] = "mara1378" +Locale["mr"]["script"] = "Deva" +Locale["mn"]["name"] = "Mongolian" +Locale["mn"]["endonym"] = "Монгол" +Locale["mn"]["translations-of"] = "%s-н орчуулга" +Locale["mn"]["definitions-of"] = "%s үгийн тодорхойлолт" +Locale["mn"]["synonyms"] = "Ойролцоо утгатай" +Locale["mn"]["examples"] = "Жишээнүүд" +Locale["mn"]["see-also"] = "Мөн харах" +Locale["mn"]["family"] = "Mongolic" +Locale["mn"]["iso"] = "mon" +Locale["mn"]["glotto"] = "mong1331" +Locale["mn"]["script"] = "Cyrl" +Locale["my"]["name"] = "Myanmar" +Locale["my"]["endonym"] = "မြန်မာစာ" +Locale["my"]["translations-of"] = "%s၏ ဘာသာပြန်ဆိုချက်များ" +Locale["my"]["definitions-of"] = "%s၏ အနက်ဖွင့်ဆိုချက်များ" +Locale["my"]["synonyms"] = "ကြောင်းတူသံကွဲများ" +Locale["my"]["examples"] = "ဥပမာ" +Locale["my"]["see-also"] = "ဖော်ပြပါများကိုလဲ ကြည့်ပါ" +Locale["my"]["family"] = "Sino-Tibetan" +Locale["my"]["iso"] = "mya" +Locale["my"]["glotto"] = "nucl1310" +Locale["my"]["script"] = "Mymr" +Locale["ne"]["name"] = "Nepali" +Locale["ne"]["endonym"] = "नेपाली" +Locale["ne"]["translations-of"] = "%sका अनुवाद" +Locale["ne"]["definitions-of"] = "%sको परिभाषा" +Locale["ne"]["synonyms"] = "समानार्थीहरू" +Locale["ne"]["examples"] = "उदाहरणहरु" +Locale["ne"]["see-also"] = "यो पनि हेर्नुहोस्" +Locale["ne"]["family"] = "Indo-European" +Locale["ne"]["iso"] = "nep" +Locale["ne"]["glotto"] = "nepa1254" +Locale["ne"]["script"] = "Deva" +Locale["no"]["name"] = "Norwegian" +Locale["no"]["endonym"] = "Norsk" +Locale["no"]["translations-of"] = "Oversettelser av %s" +Locale["no"]["definitions-of"] = "Definisjoner av %s" +Locale["no"]["synonyms"] = "Synonymer" +Locale["no"]["examples"] = "Eksempler" +Locale["no"]["see-also"] = "Se også" +Locale["no"]["family"] = "Indo-European" +Locale["no"]["iso"] = "nor" +Locale["no"]["glotto"] = "norw1258" +Locale["no"]["script"] = "Latn" +Locale["ps"]["name"] = "Pashto" +Locale["ps"]["endonym"] = "پښتو" +Locale["ps"]["translations-of"] = "د %sژباړې" +Locale["ps"]["definitions-of"] = "د%s تعریفونه" +Locale["ps"]["synonyms"] = "مترادف لغتونه" +Locale["ps"]["examples"] = "بېلګې" +Locale["ps"]["see-also"] = "دا هم ووینئ" +Locale["ps"]["family"] = "Indo-European" +Locale["ps"]["iso"] = "pus" +Locale["ps"]["glotto"] = "pash1269" +Locale["ps"]["script"] = "Arab" +Locale["ps"]["rtl"] = "true" +Locale["fa"]["name"] = "Persian" +Locale["fa"]["endonym"] = "فارسی" +Locale["fa"]["translations-of"] = "ترجمه‌های %s" +Locale["fa"]["definitions-of"] = "تعریف‌های %s" +Locale["fa"]["synonyms"] = "مترادف‌ها" +Locale["fa"]["examples"] = "مثال‌ها" +Locale["fa"]["see-also"] = "همچنین مراجعه کنید به" +Locale["fa"]["family"] = "Indo-European" +Locale["fa"]["iso"] = "fas" +Locale["fa"]["glotto"] = "west2369" +Locale["fa"]["script"] = "Arab" +Locale["fa"]["rtl"] = "true" +Locale["pl"]["name"] = "Polish" +Locale["pl"]["endonym"] = "Polski" +Locale["pl"]["translations-of"] = "Tłumaczenia %s" +Locale["pl"]["definitions-of"] = "%s – definicje" +Locale["pl"]["synonyms"] = "Synonimy" +Locale["pl"]["examples"] = "Przykłady" +Locale["pl"]["see-also"] = "Zobacz też" +Locale["pl"]["family"] = "Indo-European" +Locale["pl"]["iso"] = "pol" +Locale["pl"]["glotto"] = "poli1260" +Locale["pl"]["script"] = "Latn" +Locale["pt"]["name"] = "Portuguese" +Locale["pt"]["endonym"] = "Português" +Locale["pt"]["translations-of"] = "Traduções de %s" +Locale["pt"]["definitions-of"] = "Definições de %s" +Locale["pt"]["synonyms"] = "Sinônimos" +Locale["pt"]["examples"] = "Exemplos" +Locale["pt"]["see-also"] = "Veja também" +Locale["pt"]["family"] = "Indo-European" +Locale["pt"]["iso"] = "por" +Locale["pt"]["glotto"] = "port1283" +Locale["pt"]["script"] = "Latn" +Locale["pt"]["dictionary"] = "true" +Locale["pa"]["name"] = "Punjabi" +Locale["pa"]["endonym"] = "ਪੰਜਾਬੀ" +Locale["pa"]["translations-of"] = "ਦੇ ਅਨੁਵਾਦ%s" +Locale["pa"]["definitions-of"] = "ਦੀਆਂ ਪਰਿਭਾਸ਼ਾ %s" +Locale["pa"]["synonyms"] = "ਸਮਾਨਾਰਥਕ ਸ਼ਬਦ" +Locale["pa"]["examples"] = "ਉਦਾਹਰਣਾਂ" +Locale["pa"]["see-also"] = "ਇਹ ਵੀ ਵੇਖੋ" +Locale["pa"]["family"] = "Indo-European" +Locale["pa"]["iso"] = "pan" +Locale["pa"]["glotto"] = "panj1256" +Locale["pa"]["script"] = "Guru" +Locale["ro"]["name"] = "Romanian" +Locale["ro"]["endonym"] = "Română" +Locale["ro"]["translations-of"] = "Traduceri pentru %s" +Locale["ro"]["definitions-of"] = "Definiții pentru %s" +Locale["ro"]["synonyms"] = "Sinonime" +Locale["ro"]["examples"] = "Exemple" +Locale["ro"]["see-also"] = "Vedeți și" +Locale["ro"]["family"] = "Indo-European" +Locale["ro"]["iso"] = "ron" +Locale["ro"]["glotto"] = "roma1327" +Locale["ro"]["script"] = "Latn" +Locale["ru"]["name"] = "Russian" +Locale["ru"]["endonym"] = "Русский" +Locale["ru"]["translations-of"] = "%s: варианты перевода" +Locale["ru"]["definitions-of"] = "%s – определения" +Locale["ru"]["synonyms"] = "Синонимы" +Locale["ru"]["examples"] = "Примеры" +Locale["ru"]["see-also"] = "Похожие слова" +Locale["ru"]["family"] = "Indo-European" +Locale["ru"]["iso"] = "rus" +Locale["ru"]["glotto"] = "russ1263" +Locale["ru"]["script"] = "Cyrl" +Locale["ru"]["dictionary"] = "true" +Locale["sm"]["name"] = "Samoan" +Locale["sm"]["endonym"] = "Gagana Sāmoa" +Locale["sm"]["family"] = "Austronesian" +Locale["sm"]["iso"] = "smo" +Locale["sm"]["glotto"] = "samo1305" +Locale["sm"]["script"] = "Latn" +Locale["gd"]["name"] = "Scots Gaelic" +Locale["gd"]["endonym"] = "Gàidhlig" +Locale["gd"]["translations-of"] = "Eadar-theangachadh airson %s" +Locale["gd"]["definitions-of"] = "Deifiniseanan airson %s" +Locale["gd"]["synonyms"] = "Co-fhaclan" +Locale["gd"]["examples"] = "Buill-eisimpleir" +Locale["gd"]["see-also"] = "Faic na leanas cuideachd" +Locale["gd"]["family"] = "Indo-European" +Locale["gd"]["iso"] = "gla" +Locale["gd"]["glotto"] = "scot1245" +Locale["gd"]["script"] = "Latn" +Locale["sr-Cyrl"]["name"] = "Serbian (Cyrillic)" +Locale["sr-Cyrl"]["endonym"] = "српски" +Locale["sr-Cyrl"]["translations-of"] = "Преводи за „%s“" +Locale["sr-Cyrl"]["definitions-of"] = "Дефиниције за %s" +Locale["sr-Cyrl"]["synonyms"] = "Синоними" +Locale["sr-Cyrl"]["examples"] = "Примери" +Locale["sr-Cyrl"]["see-also"] = "Погледајте такође" +Locale["sr-Cyrl"]["family"] = "Indo-European" +Locale["sr-Cyrl"]["iso"] = "srp-Cyrl" +Locale["sr-Cyrl"]["glotto"] = "serb1264" +Locale["sr-Cyrl"]["script"] = "Cyrl" +Locale["sr-Latn"]["name"] = "Serbian (Latin)" +Locale["sr-Latn"]["endonym"] = "srpski" +Locale["sr-Latn"]["translations-of"] = "Prevodi za „%s“" +Locale["sr-Latn"]["definitions-of"] = "Definicije za %s" +Locale["sr-Latn"]["synonyms"] = "Sinonimi" +Locale["sr-Latn"]["examples"] = "Primeri" +Locale["sr-Latn"]["see-also"] = "Pogledajte takođe" +Locale["sr-Latn"]["family"] = "Indo-European" +Locale["sr-Latn"]["iso"] = "srp-Latn" +Locale["sr-Latn"]["glotto"] = "serb1264" +Locale["sr-Latn"]["script"] = "Latn" +Locale["st"]["name"] = "Sesotho" +Locale["st"]["endonym"] = "Sesotho" +Locale["st"]["translations-of"] = "Liphetolelo tsa %s" +Locale["st"]["definitions-of"] = "Meelelo ea %s" +Locale["st"]["synonyms"] = "Mantsoe a tšoanang ka moelelo" +Locale["st"]["examples"] = "Mehlala" +Locale["st"]["see-also"] = "Bona hape" +Locale["st"]["family"] = "Atlantic-Congo" +Locale["st"]["iso"] = "sot" +Locale["st"]["glotto"] = "sout2807" +Locale["st"]["script"] = "Latn" +Locale["sn"]["name"] = "Shona" +Locale["sn"]["endonym"] = "chiShona" +Locale["sn"]["translations-of"] = "Shanduro dze %s" +Locale["sn"]["definitions-of"] = "Zvinoreva %s" +Locale["sn"]["synonyms"] = "Mashoko anoreva zvakafana nemamwe" +Locale["sn"]["examples"] = "Mienzaniso" +Locale["sn"]["see-also"] = "Onawo" +Locale["sn"]["family"] = "Atlantic-Congo" +Locale["sn"]["iso"] = "sna" +Locale["sn"]["glotto"] = "core1255" +Locale["sn"]["script"] = "Latn" +Locale["sd"]["name"] = "Sindhi" +Locale["sd"]["endonym"] = "سنڌي" +Locale["sd"]["translations-of"] = "%s جو ترجمو" +Locale["sd"]["definitions-of"] = "%s جون وصفون" +Locale["sd"]["synonyms"] = "هم معني" +Locale["sd"]["examples"] = "مثالون" +Locale["sd"]["see-also"] = "به ڏسو" +Locale["sd"]["family"] = "Indo-European" +Locale["sd"]["iso"] = "snd" +Locale["sd"]["glotto"] = "sind1272" +Locale["sd"]["script"] = "Arab" +Locale["sd"]["rtl"] = "true" +Locale["si"]["name"] = "Sinhala" +Locale["si"]["endonym"] = "සිංහල" +Locale["si"]["translations-of"] = "%s හි පරිවර්තන" +Locale["si"]["definitions-of"] = "%s හි නිර්වචන" +Locale["si"]["synonyms"] = "සමානාර්ථ පද" +Locale["si"]["examples"] = "උදාහරණ" +Locale["si"]["see-also"] = "මෙයත් බලන්න" +Locale["si"]["family"] = "Indo-European" +Locale["si"]["iso"] = "sin" +Locale["si"]["glotto"] = "sinh1246" +Locale["si"]["script"] = "Sinh" +Locale["sk"]["name"] = "Slovak" +Locale["sk"]["endonym"] = "Slovenčina" +Locale["sk"]["translations-of"] = "Preklady výrazu: %s" +Locale["sk"]["definitions-of"] = "Definície výrazu %s" +Locale["sk"]["synonyms"] = "Synonymá" +Locale["sk"]["examples"] = "Príklady" +Locale["sk"]["see-also"] = "Pozrite tiež" +Locale["sk"]["family"] = "Indo-European" +Locale["sk"]["iso"] = "slk" +Locale["sk"]["glotto"] = "slov1269" +Locale["sk"]["script"] = "Latn" +Locale["sl"]["name"] = "Slovenian" +Locale["sl"]["endonym"] = "Slovenščina" +Locale["sl"]["translations-of"] = "Prevodi za %s" +Locale["sl"]["definitions-of"] = "Razlage za %s" +Locale["sl"]["synonyms"] = "Sopomenke" +Locale["sl"]["examples"] = "Primeri" +Locale["sl"]["see-also"] = "Glejte tudi" +Locale["sl"]["family"] = "Indo-European" +Locale["sl"]["iso"] = "slv" +Locale["sl"]["glotto"] = "slov1268" +Locale["sl"]["script"] = "Latn" +Locale["so"]["name"] = "Somali" +Locale["so"]["endonym"] = "Soomaali" +Locale["so"]["translations-of"] = "Turjumaada %s" +Locale["so"]["definitions-of"] = "Qeexitaannada %s" +Locale["so"]["synonyms"] = "La micne ah" +Locale["so"]["examples"] = "Tusaalooyin" +Locale["so"]["see-also"] = "Sidoo kale eeg" +Locale["so"]["family"] = "Afro-Asiatic" +Locale["so"]["iso"] = "som" +Locale["so"]["glotto"] = "soma1255" +Locale["so"]["script"] = "Latn" +Locale["es"]["name"] = "Spanish" +Locale["es"]["endonym"] = "Español" +Locale["es"]["translations-of"] = "Traducciones de %s" +Locale["es"]["definitions-of"] = "Definiciones de %s" +Locale["es"]["synonyms"] = "Sinónimos" +Locale["es"]["examples"] = "Ejemplos" +Locale["es"]["see-also"] = "Ver también" +Locale["es"]["family"] = "Indo-European" +Locale["es"]["iso"] = "spa" +Locale["es"]["glotto"] = "stan1288" +Locale["es"]["script"] = "Latn" +Locale["es"]["dictionary"] = "true" +Locale["su"]["name"] = "Sundanese" +Locale["su"]["endonym"] = "Basa Sunda" +Locale["su"]["translations-of"] = "Tarjamahan tina %s" +Locale["su"]["definitions-of"] = "Panjelasan tina %s" +Locale["su"]["synonyms"] = "Sinonim" +Locale["su"]["examples"] = "Conto" +Locale["su"]["see-also"] = "Tingali ogé" +Locale["su"]["family"] = "Austronesian" +Locale["su"]["iso"] = "sun" +Locale["su"]["glotto"] = "sund1252" +Locale["su"]["script"] = "Latn" +Locale["sw"]["name"] = "Swahili" +Locale["sw"]["endonym"] = "Kiswahili" +Locale["sw"]["translations-of"] = "Tafsiri ya %s" +Locale["sw"]["definitions-of"] = "Ufafanuzi wa %s" +Locale["sw"]["synonyms"] = "Visawe" +Locale["sw"]["examples"] = "Mifano" +Locale["sw"]["see-also"] = "Angalia pia" +Locale["sw"]["family"] = "Atlantic-Congo" +Locale["sw"]["iso"] = "swa" +Locale["sw"]["glotto"] = "swah1253" +Locale["sw"]["script"] = "Latn" +Locale["sv"]["name"] = "Swedish" +Locale["sv"]["endonym"] = "Svenska" +Locale["sv"]["translations-of"] = "Översättningar av %s" +Locale["sv"]["definitions-of"] = "Definitioner av %s" +Locale["sv"]["synonyms"] = "Synonymer" +Locale["sv"]["examples"] = "Exempel" +Locale["sv"]["see-also"] = "Se även" +Locale["sv"]["family"] = "Indo-European" +Locale["sv"]["iso"] = "swe" +Locale["sv"]["glotto"] = "swed1254" +Locale["sv"]["script"] = "Latn" +Locale["tg"]["name"] = "Tajik" +Locale["tg"]["endonym"] = "Тоҷикӣ" +Locale["tg"]["translations-of"] = "Тарҷумаҳои %s" +Locale["tg"]["definitions-of"] = "Таърифҳои %s" +Locale["tg"]["synonyms"] = "Муродифҳо" +Locale["tg"]["examples"] = "Намунаҳо:" +Locale["tg"]["see-also"] = "Ҳамчунин Бинед" +Locale["tg"]["family"] = "Indo-European" +Locale["tg"]["iso"] = "tgk" +Locale["tg"]["glotto"] = "taji1245" +Locale["tg"]["script"] = "Cyrl" +Locale["ta"]["name"] = "Tamil" +Locale["ta"]["endonym"] = "தமிழ்" +Locale["ta"]["translations-of"] = "%s இன் மொழிபெயர்ப்புகள்" +Locale["ta"]["definitions-of"] = "%s இன் வரையறைகள்" +Locale["ta"]["synonyms"] = "இணைச்சொற்கள்" +Locale["ta"]["examples"] = "எடுத்துக்காட்டுகள்" +Locale["ta"]["see-also"] = "இதையும் காண்க" +Locale["ta"]["family"] = "Dravidian" +Locale["ta"]["iso"] = "tam" +Locale["ta"]["glotto"] = "tami1289" +Locale["ta"]["script"] = "Taml" +Locale["te"]["name"] = "Telugu" +Locale["te"]["endonym"] = "తెలుగు" +Locale["te"]["translations-of"] = "%s యొక్క అనువాదాలు" +Locale["te"]["definitions-of"] = "%s యొక్క నిర్వచనాలు" +Locale["te"]["synonyms"] = "పర్యాయపదాలు" +Locale["te"]["examples"] = "ఉదాహరణలు" +Locale["te"]["see-also"] = "వీటిని కూడా చూడండి" +Locale["te"]["family"] = "Dravidian" +Locale["te"]["iso"] = "tel" +Locale["te"]["glotto"] = "telu1262" +Locale["te"]["script"] = "Telu" +Locale["th"]["name"] = "Thai" +Locale["th"]["endonym"] = "ไทย" +Locale["th"]["translations-of"] = "คำแปลของ %s" +Locale["th"]["definitions-of"] = "คำจำกัดความของ %s" +Locale["th"]["synonyms"] = "คำพ้องความหมาย" +Locale["th"]["examples"] = "ตัวอย่าง" +Locale["th"]["see-also"] = "ดูเพิ่มเติม" +Locale["th"]["family"] = "Tai-Kadai" +Locale["th"]["iso"] = "tha" +Locale["th"]["glotto"] = "thai1261" +Locale["th"]["script"] = "Thai" +Locale["tr"]["name"] = "Turkish" +Locale["tr"]["endonym"] = "Türkçe" +Locale["tr"]["translations-of"] = "%s çevirileri" +Locale["tr"]["definitions-of"] = "%s için tanımlar" +Locale["tr"]["synonyms"] = "Eş anlamlılar" +Locale["tr"]["examples"] = "Örnekler" +Locale["tr"]["see-also"] = "Ayrıca bkz." +Locale["tr"]["family"] = "Turkic" +Locale["tr"]["iso"] = "tur" +Locale["tr"]["glotto"] = "nucl1301" +Locale["tr"]["script"] = "Latn" +Locale["uk"]["name"] = "Ukrainian" +Locale["uk"]["endonym"] = "Українська" +Locale["uk"]["translations-of"] = "Переклади слова або виразу \"%s\"" +Locale["uk"]["definitions-of"] = "\"%s\" – визначення" +Locale["uk"]["synonyms"] = "Синоніми" +Locale["uk"]["examples"] = "Приклади" +Locale["uk"]["see-also"] = "Дивіться також" +Locale["uk"]["family"] = "Indo-European" +Locale["uk"]["iso"] = "ukr" +Locale["uk"]["glotto"] = "ukra1253" +Locale["uk"]["script"] = "Cyrl" +Locale["ur"]["name"] = "Urdu" +Locale["ur"]["endonym"] = "اُردُو" +Locale["ur"]["translations-of"] = "کے ترجمے %s" +Locale["ur"]["definitions-of"] = "کی تعریفات %s" +Locale["ur"]["synonyms"] = "مترادفات" +Locale["ur"]["examples"] = "مثالیں" +Locale["ur"]["see-also"] = "نیز دیکھیں" +Locale["ur"]["family"] = "Indo-European" +Locale["ur"]["iso"] = "urd" +Locale["ur"]["glotto"] = "urdu1245" +Locale["ur"]["script"] = "Arab" +Locale["ur"]["rtl"] = "true" +Locale["uz"]["name"] = "Uzbek" +Locale["uz"]["endonym"] = "Oʻzbek tili" +Locale["uz"]["translations-of"] = "%s: tarjima variantlari" +Locale["uz"]["definitions-of"] = "%s – ta’riflar" +Locale["uz"]["synonyms"] = "Sinonimlar" +Locale["uz"]["examples"] = "Namunalar" +Locale["uz"]["see-also"] = "O‘xshash so‘zlar" +Locale["uz"]["family"] = "Turkic" +Locale["uz"]["iso"] = "uzb" +Locale["uz"]["glotto"] = "uzbe1247" +Locale["uz"]["script"] = "Latn" +Locale["vi"]["name"] = "Vietnamese" +Locale["vi"]["endonym"] = "Tiếng Việt" +Locale["vi"]["translations-of"] = "Bản dịch của %s" +Locale["vi"]["definitions-of"] = "Nghĩa của %s" +Locale["vi"]["synonyms"] = "Từ đồng nghĩa" +Locale["vi"]["examples"] = "Ví dụ" +Locale["vi"]["see-also"] = "Xem thêm" +Locale["vi"]["family"] = "Austroasiatic" +Locale["vi"]["iso"] = "vie" +Locale["vi"]["glotto"] = "viet1252" +Locale["vi"]["script"] = "Latn" +Locale["cy"]["name"] = "Welsh" +Locale["cy"]["endonym"] = "Cymraeg" +Locale["cy"]["translations-of"] = "Cyfieithiadau %s" +Locale["cy"]["definitions-of"] = "Diffiniadau %s" +Locale["cy"]["synonyms"] = "Cyfystyron" +Locale["cy"]["examples"] = "Enghreifftiau" +Locale["cy"]["see-also"] = "Gweler hefyd" +Locale["cy"]["family"] = "Indo-European" +Locale["cy"]["iso"] = "cym" +Locale["cy"]["glotto"] = "wels1247" +Locale["cy"]["script"] = "Latn" +Locale["fy"]["name"] = "Frisian" +Locale["fy"]["endonym"] = "Frysk" +Locale["fy"]["translations-of"] = "Oersettings fan %s" +Locale["fy"]["definitions-of"] = "Definysjes fan %s" +Locale["fy"]["synonyms"] = "Synonimen" +Locale["fy"]["examples"] = "Foarbylden" +Locale["fy"]["see-also"] = "Sjoch ek" +Locale["fy"]["family"] = "Indo-European" +Locale["fy"]["iso"] = "fry" +Locale["fy"]["glotto"] = "west2354" +Locale["fy"]["script"] = "Latn" +Locale["xh"]["name"] = "Xhosa" +Locale["xh"]["endonym"] = "isiXhosa" +Locale["xh"]["translations-of"] = "Iinguqulelo zika-%s" +Locale["xh"]["definitions-of"] = "Iingcaciso zika-%s" +Locale["xh"]["synonyms"] = "Izithethantonye" +Locale["xh"]["examples"] = "Imizekelo" +Locale["xh"]["see-also"] = "Kwakhona bona" +Locale["xh"]["family"] = "Atlantic-Congo" +Locale["xh"]["iso"] = "xho" +Locale["xh"]["glotto"] = "xhos1239" +Locale["xh"]["script"] = "Latn" +Locale["yi"]["name"] = "Yiddish" +Locale["yi"]["endonym"] = "ייִדיש" +Locale["yi"]["translations-of"] = "איבערזעצונגען פון %s" +Locale["yi"]["definitions-of"] = "דפיניציונען %s" +Locale["yi"]["synonyms"] = "סינאָנימען" +Locale["yi"]["examples"] = "ביישפילע" +Locale["yi"]["see-also"] = "זייען אויך" +Locale["yi"]["family"] = "Indo-European" +Locale["yi"]["iso"] = "yid" +Locale["yi"]["glotto"] = "yidd1255" +Locale["yi"]["script"] = "Hebr" +Locale["yi"]["rtl"] = "true" +Locale["yo"]["name"] = "Yoruba" +Locale["yo"]["endonym"] = "Yorùbá" +Locale["yo"]["translations-of"] = "Awọn itumọ ti %s" +Locale["yo"]["definitions-of"] = "Awọn itumọ ti %s" +Locale["yo"]["synonyms"] = "Awọn ọrọ onitumọ" +Locale["yo"]["examples"] = "Awọn apẹrẹ" +Locale["yo"]["see-also"] = "Tun wo" +Locale["yo"]["family"] = "Atlantic-Congo" +Locale["yo"]["iso"] = "yor" +Locale["yo"]["glotto"] = "yoru1245" +Locale["yo"]["script"] = "Latn" +Locale["zu"]["name"] = "Zulu" +Locale["zu"]["endonym"] = "isiZulu" +Locale["zu"]["translations-of"] = "Ukuhumusha i-%s" +Locale["zu"]["definitions-of"] = "Izincazelo ze-%s" +Locale["zu"]["synonyms"] = "Amagama afanayo" +Locale["zu"]["examples"] = "Izibonelo" +Locale["zu"]["see-also"] = "Bheka futhi" +Locale["zu"]["family"] = "Atlantic-Congo" +Locale["zu"]["iso"] = "zul" +Locale["zu"]["glotto"] = "zulu1248" +Locale["zu"]["script"] = "Latn" +Locale["yue"]["support"] = "bing-only" +Locale["yue"]["name"] = "Cantonese" +Locale["yue"]["endonym"] = "粵語" +Locale["yue"]["family"] = "Sino-Tibetan" +Locale["yue"]["iso"] = "yue" +Locale["yue"]["glotto"] = "cant1236" +Locale["yue"]["script"] = "Hant" +Locale["fj"]["support"] = "bing-only" +Locale["fj"]["name"] = "Fijian" +Locale["fj"]["endonym"] = "Vosa Vakaviti" +Locale["fj"]["family"] = "Austronesian" +Locale["fj"]["iso"] = "fij" +Locale["fj"]["glotto"] = "fiji1243" +Locale["fj"]["script"] = "Latn" +Locale["mww"]["support"] = "bing-only" +Locale["mww"]["name"] = "Hmong Daw" +Locale["mww"]["endonym"] = "Hmoob Daw" +Locale["mww"]["family"] = "Hmong-Mien" +Locale["mww"]["iso"] = "mww" +Locale["mww"]["glotto"] = "hmon1333" +Locale["mww"]["script"] = "Latn" +Locale["otq"]["support"] = "bing-only" +Locale["otq"]["name"] = "Querétaro Otomi" +Locale["otq"]["endonym"] = "Hñąñho" +Locale["otq"]["family"] = "Oto-Manguean" +Locale["otq"]["iso"] = "otq" +Locale["otq"]["glotto"] = "quer1236" +Locale["otq"]["script"] = "Latn" +Locale["ty"]["support"] = "bing-only" +Locale["ty"]["name"] = "Tahitian" +Locale["ty"]["endonym"] = "Reo Tahiti" +Locale["ty"]["family"] = "Austronesian" +Locale["ty"]["iso"] = "tah" +Locale["ty"]["glotto"] = "tahi1242" +Locale["ty"]["script"] = "Latn" +Locale["to"]["support"] = "bing-only" +Locale["to"]["name"] = "Tongan" +Locale["to"]["endonym"] = "Lea faka-Tonga" +Locale["to"]["family"] = "Austronesian" +Locale["to"]["iso"] = "ton" +Locale["to"]["glotto"] = "tong1325" +Locale["to"]["script"] = "Latn" +Locale["yua"]["support"] = "bing-only" +Locale["yua"]["name"] = "Yucatec Maya" +Locale["yua"]["endonym"] = "Màaya T'àan" +Locale["yua"]["family"] = "Mayan" +Locale["yua"]["iso"] = "yua" +Locale["yua"]["glotto"] = "yuca1254" +Locale["yua"]["script"] = "Latn" +Locale["tlh"]["support"] = "bing-only" +Locale["tlh"]["name"] = "Klingon" +Locale["tlh"]["endonym"] = "tlhIngan Hol" +Locale["tlh"]["family"] = "Artificial Language" +Locale["tlh"]["iso"] = "tlh" +Locale["tlh"]["script"] = "Latn" +Locale["tlh-Qaak"]["support"] = "bing-only" +Locale["tlh-Qaak"]["name"] = "Klingon (pIqaD)" +Locale["tlh-Qaak"]["endonym"] = " " +Locale["tlh-Qaak"]["family"] = "Artificial Language" +Locale["tlh-Qaak"]["iso"] = "tlh" +Locale["tlh-Qaak"]["script"] = "Piqd" +Locale["as"]["support"] = "unstable" +Locale["as"]["name"] = "Assamese" +Locale["as"]["endonym"] = "অসমীয়া" +Locale["as"]["family"] = "Indo-European" +Locale["as"]["iso"] = "asm" +Locale["as"]["glotto"] = "assa1263" +Locale["as"]["script"] = "Beng" +Locale["ba"]["support"] = "yandex-only" +Locale["ba"]["name"] = "Bashkir" +Locale["ba"]["endonym"] = "башҡорт теле" +Locale["ba"]["family"] = "Turkic" +Locale["ba"]["iso"] = "bak" +Locale["ba"]["glotto"] = "bash1264" +Locale["ba"]["script"] = "Cyrl" +Locale["br"]["support"] = "unstable" +Locale["br"]["name"] = "Breton" +Locale["br"]["endonym"] = "Brezhoneg" +Locale["br"]["family"] = "Indo-European" +Locale["br"]["iso"] = "bre" +Locale["br"]["glotto"] = "bret1244" +Locale["br"]["script"] = "Latn" +Locale["dz"]["support"] = "unstable" +Locale["dz"]["name"] = "Dzongkha" +Locale["dz"]["endonym"] = "རྫོང་ཁ" +Locale["dz"]["family"] = "Sino-Tibetan" +Locale["dz"]["iso"] = "dzo" +Locale["dz"]["glotto"] = "nucl1307" +Locale["dz"]["script"] = "Tibt" +Locale["mhr"]["support"] = "yandex-only" +Locale["mhr"]["name"] = "Eastern Mari" +Locale["mhr"]["endonym"] = "Олык марий" +Locale["mhr"]["family"] = "Uralic" +Locale["mhr"]["iso"] = "mhr" +Locale["mhr"]["glotto"] = "east2328" +Locale["mhr"]["script"] = "Cyrl" +Locale["fo"]["support"] = "unstable" +Locale["fo"]["name"] = "Faroese" +Locale["fo"]["endonym"] = "Føroyskt" +Locale["fo"]["family"] = "Indo-European" +Locale["fo"]["iso"] = "fao" +Locale["fo"]["glotto"] = "faro1244" +Locale["fo"]["script"] = "Latn" +Locale["gn"]["support"] = "unstable" +Locale["gn"]["name"] = "Guarani" +Locale["gn"]["endonym"] = "Avañe'ẽ" +Locale["gn"]["family"] = "Tupian" +Locale["gn"]["iso"] = "grn" +Locale["gn"]["glotto"] = "para1311" +Locale["gn"]["script"] = "Latn" +Locale["mrj"]["support"] = "yandex-only" +Locale["mrj"]["name"] = "Hill Mari" +Locale["mrj"]["endonym"] = "Кырык мары" +Locale["mrj"]["family"] = "Uralic" +Locale["mrj"]["iso"] = "mrj" +Locale["mrj"]["glotto"] = "west2392" +Locale["mrj"]["script"] = "Cyrl" +Locale["ie"]["support"] = "unstable" +Locale["ie"]["name"] = "Interlingue" +Locale["ie"]["endonym"] = "Interlingue" +Locale["ie"]["family"] = "Artificial Language" +Locale["ie"]["iso"] = "ile" +Locale["ie"]["glotto"] = "occi1241" +Locale["ie"]["script"] = "Latn" +Locale["rw"]["support"] = "unstable" +Locale["rw"]["name"] = "Kinyarwanda" +Locale["rw"]["endonym"] = "Ikinyarwanda" +Locale["rw"]["family"] = "Atlantic-Congo" +Locale["rw"]["iso"] = "kin" +Locale["rw"]["glotto"] = "kiny1244" +Locale["rw"]["script"] = "Latn" +Locale["oc"]["support"] = "unstable" +Locale["oc"]["name"] = "Occitan" +Locale["oc"]["endonym"] = "Occitan" +Locale["oc"]["family"] = "Indo-European" +Locale["oc"]["iso"] = "oci" +Locale["oc"]["glotto"] = "occi1239" +Locale["oc"]["script"] = "Latn" +Locale["om"]["support"] = "unstable" +Locale["om"]["name"] = "Oromo" +Locale["om"]["endonym"] = "Afaan Oromoo" +Locale["om"]["family"] = "Afro-Asiatic" +Locale["om"]["iso"] = "orm" +Locale["om"]["glotto"] = "nucl1736" +Locale["om"]["script"] = "Latn" +Locale["or"]["support"] = "unstable" +Locale["or"]["name"] = "Oriya" +Locale["or"]["endonym"] = "ଓଡ଼ିଆ" +Locale["or"]["family"] = "Indo-European" +Locale["or"]["iso"] = "ori" +Locale["or"]["glotto"] = "macr1269" +Locale["or"]["script"] = "Orya" +Locale["pap"]["support"] = "yandex-only" +Locale["pap"]["name"] = "Papiamento" +Locale["pap"]["endonym"] = "Papiamentu" +Locale["pap"]["family"] = "Indo-European" +Locale["pap"]["iso"] = "pap" +Locale["pap"]["glotto"] = "papi1253" +Locale["pap"]["script"] = "Latn" +Locale["rm"]["support"] = "unstable" +Locale["rm"]["name"] = "Romansh" +Locale["rm"]["endonym"] = "Rumantsch" +Locale["rm"]["family"] = "Indo-European" +Locale["rm"]["iso"] = "roh" +Locale["rm"]["glotto"] = "roma1326" +Locale["rm"]["script"] = "Latn" +Locale["ti"]["support"] = "unstable" +Locale["ti"]["name"] = "Tigrinya" +Locale["ti"]["endonym"] = "ትግርኛ" +Locale["ti"]["family"] = "Afro-Asiatic" +Locale["ti"]["iso"] = "tir" +Locale["ti"]["glotto"] = "tigr1271" +Locale["ti"]["script"] = "Ethi" +Locale["bo"]["support"] = "unstable" +Locale["bo"]["name"] = "Tibetan" +Locale["bo"]["endonym"] = "བོད་ཡིག" +Locale["bo"]["family"] = "Sino-Tibetan" +Locale["bo"]["iso"] = "bod" +Locale["bo"]["glotto"] = "tibe1272" +Locale["bo"]["script"] = "Tibt" +Locale["tk"]["support"] = "unstable" +Locale["tk"]["name"] = "Turkmen" +Locale["tk"]["endonym"] = "Türkmen" +Locale["tk"]["family"] = "Turkic" +Locale["tk"]["iso"] = "tuk" +Locale["tk"]["glotto"] = "turk1304" +Locale["tk"]["script"] = "Latn" +Locale["tt"]["support"] = "yandex-only" +Locale["tt"]["name"] = "Tatar" +Locale["tt"]["endonym"] = "татарча" +Locale["tt"]["family"] = "Turkic" +Locale["tt"]["iso"] = "tat" +Locale["tt"]["glotto"] = "tata1255" +Locale["tt"]["script"] = "Cyrl" +Locale["udm"]["support"] = "yandex-only" +Locale["udm"]["name"] = "Udmurt" +Locale["udm"]["endonym"] = "удмурт" +Locale["udm"]["family"] = "Uralic" +Locale["udm"]["iso"] = "udm" +Locale["udm"]["glotto"] = "udmu1245" +Locale["udm"]["script"] = "Cyrl" +Locale["ug"]["support"] = "unstable" +Locale["ug"]["name"] = "Uyghur" +Locale["ug"]["endonym"] = "ئۇيغۇر تىلى" +Locale["ug"]["family"] = "Turkic" +Locale["ug"]["iso"] = "uig" +Locale["ug"]["glotto"] = "uigh1240" +Locale["ug"]["script"] = "Arab" +Locale["ug"]["rtl"] = "true" +Locale["vo"]["support"] = "unstable" +Locale["vo"]["name"] = "Volapük" +Locale["vo"]["endonym"] = "Volapük" +Locale["vo"]["family"] = "Artificial Language" +Locale["vo"]["iso"] = "vol" +Locale["vo"]["script"] = "Latn" +Locale["wo"]["support"] = "unstable" +Locale["wo"]["name"] = "Wolof" +Locale["wo"]["endonym"] = "Wollof" +Locale["wo"]["family"] = "Atlantic-Congo" +Locale["wo"]["iso"] = "wol" +Locale["wo"]["glotto"] = "wolo1247" +Locale["wo"]["script"] = "Latn" +Locale["chr"]["support"] = "unstable" +Locale["chr"]["name"] = "Cherokee" +Locale["chr"]["endonym"] = "ᏣᎳᎩ" +Locale["chr"]["family"] = "Iroquoian" +Locale["chr"]["iso"] = "chr" +Locale["chr"]["glotto"] = "cher1273" +Locale["chr"]["script"] = "Cher" +Locale["emj"]["support"] = "yandex-only" +Locale["emj"]["name"] = "Emoji" +Locale["emj"]["endonym"] = "Emoji" +} +function initLocaleAlias( i) { +for (i in Locale) { +LocaleAlias[Locale[i]["iso"]] = i +LocaleAlias[tolower(Locale[i]["name"])] = i +LocaleAlias[tolower(Locale[i]["endonym"])] = i +} +LocaleAlias["in"] = "id" +LocaleAlias["iw"] = "he" +LocaleAlias["ji"] = "yi" +LocaleAlias["jw"] = "jv" +LocaleAlias["mo"] = "ro" +LocaleAlias["nb"] = "no" +LocaleAlias["nn"] = "no" +LocaleAlias["sh"] = "sr-Cyrl" +LocaleAlias["sr"] = "sr-Cyrl" +LocaleAlias["srp"] = "sr-Cyrl" +LocaleAlias["serbian"] = "sr-Cyrl" +LocaleAlias["zh"] = "zh-CN" +LocaleAlias["zh-CHS"] = "zh-CN" +LocaleAlias["zh-CHT"] = "zh-TW" +LocaleAlias["zh-Hans"] = "zh-CN" +LocaleAlias["zh-Hant"] = "zh-TW" +LocaleAlias["zho"] = "zh-CN" +LocaleAlias["chinese"] = "zh-CN" +LocaleAlias["tlh-Latn"] = "tlh" +LocaleAlias["tlh-Piqd"] = "tlh-Qaak" +} +function initLocaleDisplay( i) { +for (i in Locale) { +Locale[i]["display"] = show(Locale[i]["endonym"], i) +} +} +function getCode(code, group) { +if (code == "auto" || code in Locale) +return code +else if (code in LocaleAlias) +return LocaleAlias[code] +else if (tolower(code) in LocaleAlias) +return LocaleAlias[tolower(code)] +match(code, /^([[:alpha:]][[:alpha:]][[:alpha:]]?)-(.*)$/, group) +if (group[1]) +return group[1] +return +} +function getName(code) { +return Locale[getCode(code)]["name"] +} +function getEndonym(code) { +return Locale[getCode(code)]["endonym"] +} +function getDisplay(code) { +return Locale[getCode(code)]["display"] +} +function showTranslationsOf(code, text, fmt) { +fmt = Locale[getCode(code)]["translations-of"] +if (!fmt) fmt = Locale["en"]["translations-of"] +return sprintf(fmt, text) +} +function showDefinitionsOf(code, text, fmt) { +fmt = Locale[getCode(code)]["definitions-of"] +if (!fmt) fmt = Locale["en"]["definitions-of"] +return sprintf(fmt, text) +} +function showSynonyms(code, tmp) { +tmp = Locale[getCode(code)]["synonyms"] +if (!tmp) tmp = Locale["en"]["synonyms"] +return tmp +} +function showExamples(code, tmp) { +tmp = Locale[getCode(code)]["examples"] +if (!tmp) tmp = Locale["en"]["examples"] +return tmp +} +function showSeeAlso(code, tmp) { +tmp = Locale[getCode(code)]["see-also"] +if (!tmp) tmp = Locale["en"]["see-also"] +return tmp +} +function getFamily(code) { +return Locale[getCode(code)]["family"] +} +function getISO(code) { +return Locale[getCode(code)]["iso"] +} +function getGlotto(code) { +return Locale[getCode(code)]["glotto"] +} +function getScript(code) { +return Locale[getCode(code)]["script"] +} +function isRTL(code) { +return Locale[getCode(code)]["rtl"] ? 1 : 0 +} +function hasDictionary(code) { +return Locale[getCode(code)]["dictionary"] ? 1 : 0 +} +function compName(i1, v1, i2, v2) { +if (getName(i1) < getName(i2)) +return -1 +else +return (getName(i1) != getName(i2)) +} +function scriptName(code) { +switch (code) { +case "Arab": return "Arabic" +case "Armn": return "Armenian" +case "Beng": return "Bengali" +case "Cher": return "Cherokee" +case "Cyrl": return "Cyrillic" +case "Deva": return "Devanagari" +case "Ethi": return "Ethiopic (Geʻez)" +case "Geor": return "Georgian (Mkhedruli)" +case "Grek": return "Greek" +case "Gujr": return "Gujarati" +case "Guru": return "Gurmukhi" +case "Hani": return "Han" +case "Hans": return "Han (Simplified)" +case "Hant": return "Han (Traditional)" +case "Hebr": return "Hebrew" +case "Jpan": return "Japanese (Han + Hiragana + Katakana)" +case "Khmr": return "Khmer" +case "Knda": return "Kannada" +case "Kore": return "Korean (Hangul + Han)" +case "Laoo": return "Lao" +case "Latn": return "Latin" +case "Mlym": return "Malayalam" +case "Mymr": return "Myanmar" +case "Orya": return "Oriya" +case "Piqd": return "Klingon (pIqaD)" +case "Sinh": return "Sinhala" +case "Taml": return "Tamil" +case "Telu": return "Telugu" +case "Thai": return "Thai" +case "Tibt": return "Tibetan" +default: return "Unknown" +} +} +function getDetails(code, group, iso, language, script) { +if (code == "auto" || !getCode(code)) { +e("[ERROR] Language not found: " code "\n"\ +" Run '-reference / -R' to see a list of available languages.") +exit 1 +} +script = scriptName(getScript(code)) +if (isRTL(code)) script = script " (R-to-L)" +split(getISO(code), group, "-") +iso = group[1] +split(getName(code), group, " ") +language = length(group) == 1 ? group[1] "_language" : +group[2] ~ /^\(.*\)$/ ? group[1] "_language" : join(group, "_") +return ansi("bold", sprintf("%s\n", getDisplay(code)))\ +sprintf("%-22s%s\n", "Name", ansi("bold", getName(code)))\ +sprintf("%-22s%s\n", "Family", ansi("bold", getFamily(code)))\ +sprintf("%-22s%s\n", "Writing system", ansi("bold", script))\ +sprintf("%-22s%s\n", "Code", ansi("bold", getCode(code)))\ +sprintf("%-22s%s\n", "ISO 639-3", ansi("bold", iso))\ +sprintf("%-22s%s\n", "SIL", ansi("bold", "http://www-01.sil.org/iso639-3/documentation.asp?id=" iso))\ +sprintf("%-22s%s\n", "Glottolog", getGlotto(code) ? +ansi("bold", "http://glottolog.org/resource/languoid/id/" getGlotto(code)) : "")\ +sprintf("%-22s%s", "Wikipedia", ansi("bold", "http://en.wikipedia.org/wiki/" language)) +} +function showPhonetics(phonetics, code) { +if (code && getCode(code) == "en") +return "/" phonetics "/" +else +return "(" phonetics ")" +} +function show(text, code, command, temp) { +if (!code || isRTL(code)) { +if (Cache[text][0]) +return Cache[text][0] +else { +if ((FriBidi || (code && isRTL(code))) && BiDiNoPad) { +command = "echo " parameterize(text) PIPE BiDiNoPad +command | getline temp +close(command) +} else +temp = text +return Cache[text][0] = temp +} +} else +return text +} +function s(text, code, width, command, temp) { +if (!code || isRTL(code)) { +if (!width) width = Option["width"] +if (Cache[text][width]) +return Cache[text][width] +else { +if ((FriBidi || (code && isRTL(code))) && BiDi) { +command = "echo " parameterize(text) PIPE sprintf(BiDi, width) +command | getline temp +close(command) +} else +temp = text +return Cache[text][width] = temp +} +} else +return text +} +function ins(level, text, code, width, i, temp) { +if (code && isRTL(code)) { +if (!width) width = Option["width"] +return s(text, code, width - Option["indent"] * level) +} else +return replicate(" ", Option["indent"] * level) text +} +function parseLang(lang, code, group) { +match(lang, /^([a-z][a-z][a-z]?)(_|$)/, group) +code = getCode(group[1]) +if (lang ~ /^zh_(CN|SG)/) code = "zh-CN" +else if (lang ~ /^zh_(TW|HK)/) code = "zh-TW" +if (!code) code = "en" +return code +} +function initUserLang( lang, utf) { +if (lang = ENVIRON["LC_ALL"]) { +if (!UserLocale) UserLocale = lang +utf = utf || tolower(lang) ~ /utf-?8$/ +} +if (lang = ENVIRON["LANG"]) { +if (!UserLocale) UserLocale = lang +utf = utf || tolower(lang) ~ /utf-?8$/ +} +if (!UserLocale) { +UserLocale = "en_US.UTF-8" +utf = 1 +} +if (!utf) +w("[WARNING] Your locale codeset (" UserLocale ") is not UTF-8.") +UserLang = parseLang(UserLocale) +} +function getVersion( build, gitHead) { +initAudioPlayer() +initPager() +Platform = Platform ? Platform : detectProgram("uname", "-s", 1) +if (ENVIRON["TRANS_BUILD"]) +build = "-" ENVIRON["TRANS_BUILD"] +else { +gitHead = getGitHead() +build = gitHead ? "-git:" gitHead : "" +} +return ansi("bold", sprintf("%-22s%s%s\n\n", Name, Version, build))\ +sprintf("%-22s%s\n", "platform", Platform)\ +sprintf("%-22s%s\n", "terminal type", ENVIRON["TERM"])\ +sprintf("%-22s%s\n", "bi-di emulator", BiDiTerm ? BiDiTerm : +"[N/A]")\ +sprintf("%-22s%s\n", "gawk (GNU Awk)", PROCINFO["version"])\ +sprintf("%s\n", FriBidi ? FriBidi : +"fribidi (GNU FriBidi) [NOT INSTALLED]")\ +sprintf("%-22s%s\n", "audio player", AudioPlayer ? AudioPlayer : +"[NOT INSTALLED]")\ +sprintf("%-22s%s\n", "terminal pager", Pager ? Pager : +"[NOT INSTALLED]")\ +sprintf("%-22s%s\n", "web browser", Option["browser"] != NONE ? +Option["browser"] :"[NONE]")\ +sprintf("%-22s%s (%s)\n", "user locale", UserLocale, getName(UserLang))\ +sprintf("%-22s%s\n", "home language", Option["hl"])\ +sprintf("%-22s%s\n", "source language", join(Option["sls"], "+"))\ +sprintf("%-22s%s\n", "target language", join(Option["tl"], "+"))\ +sprintf("%-22s%s\n", "translation engine", Option["engine"])\ +sprintf("%-22s%s\n", "proxy", Option["proxy"] ? Option["proxy"] : +"[NONE]")\ +sprintf("%-22s%s\n", "user-agent", Option["user-agent"] ? Option["user-agent"] : +"[NONE]")\ +sprintf("%-22s%s\n", "ip version", Option["ip-version"] ? Option["ip-version"] : +"[DEFAULT]")\ +sprintf("%-22s%s\n", "theme", Option["theme"])\ +sprintf("%-22s%s\n", "init file", InitScript ? InitScript : "[NONE]")\ +sprintf("\n%-22s%s", "Report bugs to:", "https://github.com/soimort/translate-shell/issues") +} +function getHelp() { +return "Usage: " ansi("bold", Command)\ +" [" ansi("underline", "OPTIONS") "]"\ +" [" ansi("underline", "SOURCES") "]"\ +":[" ansi("underline", "TARGETS") "]"\ +" [" ansi("underline", "TEXT") "]..." RS\ +RS "Information options:" RS\ +ins(1, ansi("bold", "-V") ", " ansi("bold", "-version")) RS\ +ins(2, "Print version and exit.") RS\ +ins(1, ansi("bold", "-H") ", " ansi("bold", "-help")) RS\ +ins(2, "Print help message and exit.") RS\ +ins(1, ansi("bold", "-M") ", " ansi("bold", "-man")) RS\ +ins(2, "Show man page and exit.") RS\ +ins(1, ansi("bold", "-T") ", " ansi("bold", "-reference")) RS\ +ins(2, "Print reference table of languages and exit.") RS\ +ins(1, ansi("bold", "-R") ", " ansi("bold", "-reference-english")) RS\ +ins(2, "Print reference table of languages (in English names) and exit.") RS\ +ins(1, ansi("bold", "-L ") ansi("underline", "CODES")\ +", " ansi("bold", "-list ") ansi("underline", "CODES")) RS\ +ins(2, "Print details of languages and exit.") RS\ +ins(1, ansi("bold", "-S") ", " ansi("bold", "-list-engines")) RS\ +ins(2, "List available translation engines and exit.") RS\ +ins(1, ansi("bold", "-U") ", " ansi("bold", "-upgrade")) RS\ +ins(2, "Check for upgrade of this program.") RS\ +RS "Translator options:" RS\ +ins(1, ansi("bold", "-e ") ansi("underline", "ENGINE")\ +", " ansi("bold", "-engine ") ansi("underline", "ENGINE")) RS\ +ins(2, "Specify the translation engine to use.") RS\ +RS "Display options:" RS\ +ins(1, ansi("bold", "-verbose")) RS\ +ins(2, "Verbose mode. (default)") RS\ +ins(1, ansi("bold", "-b") ", " ansi("bold", "-brief")) RS\ +ins(2, "Brief mode.") RS\ +ins(1, ansi("bold", "-d") ", " ansi("bold", "-dictionary")) RS\ +ins(2, "Dictionary mode.") RS\ +ins(1, ansi("bold", "-identify")) RS\ +ins(2, "Language identification.") RS\ +ins(1, ansi("bold", "-show-original ") ansi("underline", "Y/n")) RS\ +ins(2, "Show original text or not.") RS\ +ins(1, ansi("bold", "-show-original-phonetics ") ansi("underline", "Y/n")) RS\ +ins(2, "Show phonetic notation of original text or not.") RS\ +ins(1, ansi("bold", "-show-translation ") ansi("underline", "Y/n")) RS\ +ins(2, "Show translation or not.") RS\ +ins(1, ansi("bold", "-show-translation-phonetics ") ansi("underline", "Y/n")) RS\ +ins(2, "Show phonetic notation of translation or not.") RS\ +ins(1, ansi("bold", "-show-prompt-message ") ansi("underline", "Y/n")) RS\ +ins(2, "Show prompt message or not.") RS\ +ins(1, ansi("bold", "-show-languages ") ansi("underline", "Y/n")) RS\ +ins(2, "Show source and target languages or not.") RS\ +ins(1, ansi("bold", "-show-original-dictionary ") ansi("underline", "y/N")) RS\ +ins(2, "Show dictionary entry of original text or not.") RS\ +ins(1, ansi("bold", "-show-dictionary ") ansi("underline", "Y/n")) RS\ +ins(2, "Show dictionary entry of translation or not.") RS\ +ins(1, ansi("bold", "-show-alternatives ") ansi("underline", "Y/n")) RS\ +ins(2, "Show alternative translations or not.") RS\ +ins(1, ansi("bold", "-w ") ansi("underline", "NUM")\ +", " ansi("bold", "-width ") ansi("underline", "NUM")) RS\ +ins(2, "Specify the screen width for padding.") RS\ +ins(1, ansi("bold", "-indent ") ansi("underline", "NUM")) RS\ +ins(2, "Specify the size of indent (number of spaces).") RS\ +ins(1, ansi("bold", "-theme ") ansi("underline", "FILENAME")) RS\ +ins(2, "Specify the theme to use.") RS\ +ins(1, ansi("bold", "-no-theme")) RS\ +ins(2, "Do not use any other theme than default.") RS\ +ins(1, ansi("bold", "-no-ansi")) RS\ +ins(2, "Do not use ANSI escape codes.") RS\ +ins(1, ansi("bold", "-no-autocorrect")) RS\ +ins(2, "Do not autocorrect. (if defaulted by the translation engine)") RS\ +ins(1, ansi("bold", "-no-bidi")) RS\ +ins(2, "Do not convert bidirectional texts.") RS\ +ins(1, ansi("bold", "-bidi")) RS\ +ins(2, "Always convert bidirectional texts.") RS\ +ins(1, ansi("bold", "-no-warn")) RS\ +ins(2, "Do not write warning messages to stderr.") RS\ +ins(1, ansi("bold", "-dump")) RS\ +ins(2, "Print raw API response instead.") RS\ +RS "Audio options:" RS\ +ins(1, ansi("bold", "-p, -play")) RS\ +ins(2, "Listen to the translation.") RS\ +ins(1, ansi("bold", "-speak")) RS\ +ins(2, "Listen to the original text.") RS\ +ins(1, ansi("bold", "-n ") ansi("underline", "VOICE")\ +", " ansi("bold", "-narrator ") ansi("underline", "VOICE")) RS\ +ins(2, "Specify the narrator, and listen to the translation.") RS\ +ins(1, ansi("bold", "-player ") ansi("underline", "PROGRAM")) RS\ +ins(2, "Specify the audio player to use, and listen to the translation.") RS\ +ins(1, ansi("bold", "-no-play")) RS\ +ins(2, "Do not listen to the translation.") RS\ +ins(1, ansi("bold", "-no-translate")) RS\ +ins(2, "Do not translate anything when using -speak.") RS\ +ins(1, ansi("bold", "-download-audio")) RS\ +ins(2, "Download the audio to the current directory.") RS\ +ins(1, ansi("bold", "-download-audio-as ") ansi("underline", "FILENAME")) RS\ +ins(2, "Download the audio to the specified file.") RS\ +RS "Terminal paging and browsing options:" RS\ +ins(1, ansi("bold", "-v") ", " ansi("bold", "-view")) RS\ +ins(2, "View the translation in a terminal pager.") RS\ +ins(1, ansi("bold", "-pager ") ansi("underline", "PROGRAM")) RS\ +ins(2, "Specify the terminal pager to use, and view the translation.") RS\ +ins(1, ansi("bold", "-no-view") ", " ansi("bold", "-no-pager")) RS\ +ins(2, "Do not view the translation in a terminal pager.") RS\ +ins(1, ansi("bold", "-browser ") ansi("underline", "PROGRAM")) RS\ +ins(2, "Specify the web browser to use.") RS\ +ins(1, ansi("bold", "-no-browser")) RS\ +ins(2, "Do not open the web browser.") RS\ +RS "Networking options:" RS\ +ins(1, ansi("bold", "-x ") ansi("underline", "HOST:PORT")\ +", " ansi("bold", "-proxy ") ansi("underline", "HOST:PORT")) RS\ +ins(2, "Use HTTP proxy on given port.") RS\ +ins(1, ansi("bold", "-u ") ansi("underline", "STRING")\ +", " ansi("bold", "-user-agent ") ansi("underline", "STRING")) RS\ +ins(2, "Specify the User-Agent to identify as.") RS\ +ins(1, ansi("bold", "-4") ", " ansi("bold", "-ipv4")\ +", " ansi("bold", "-inet4-only")) RS\ +ins(2, "Connect only to IPv4 addresses.") RS\ +ins(1, ansi("bold", "-6") ", " ansi("bold", "-ipv6")\ +", " ansi("bold", "-inet6-only")) RS\ +ins(2, "Connect only to IPv6 addresses.") RS\ +RS "Interactive shell options:" RS\ +ins(1, ansi("bold", "-I") ", " ansi("bold", "-interactive") ", " ansi("bold", "-shell")) RS\ +ins(2, "Start an interactive shell.") RS\ +ins(1, ansi("bold", "-E") ", " ansi("bold", "-emacs")) RS\ +ins(2, "Start the GNU Emacs front-end for an interactive shell.") RS\ +ins(1, ansi("bold", "-no-rlwrap")) RS\ +ins(2, "Do not invoke rlwrap when starting an interactive shell.") RS\ +RS "I/O options:" RS\ +ins(1, ansi("bold", "-i ") ansi("underline", "FILENAME")\ +", " ansi("bold", "-input ") ansi("underline", "FILENAME")) RS\ +ins(2, "Specify the input file.") RS\ +ins(1, ansi("bold", "-o ") ansi("underline", "FILENAME")\ +", " ansi("bold", "-output ") ansi("underline", "FILENAME")) RS\ +ins(2, "Specify the output file.") RS\ +RS "Language preference options:" RS\ +ins(1, ansi("bold", "-l ") ansi("underline", "CODE")\ +", " ansi("bold", "-hl ") ansi("underline", "CODE")\ +", " ansi("bold", "-lang ") ansi("underline", "CODE")) RS\ +ins(2, "Specify your home language.") RS\ +ins(1, ansi("bold", "-s ") ansi("underline", "CODES")\ +", " ansi("bold", "-sl ") ansi("underline", "CODES")\ +", " ansi("bold", "-source ") ansi("underline", "CODES")\ +", " ansi("bold", "-from ") ansi("underline", "CODES")) RS\ +ins(2, "Specify the source language(s), joined by '+'.") RS\ +ins(1, ansi("bold", "-t ") ansi("underline", "CODES")\ +", " ansi("bold", "-tl ") ansi("underline", "CODES")\ +", " ansi("bold", "-target ") ansi("underline", "CODES")\ +", " ansi("bold", "-to ") ansi("underline", "CODES")) RS\ +ins(2, "Specify the target language(s), joined by '+'.") RS\ +RS "Text preprocessing options:" RS\ +ins(1, ansi("bold", "-j") ", " ansi("bold", "-join-sentence")) RS\ +ins(2, "Treat all arguments as one single sentence.") RS\ +RS "Other options:" RS\ +ins(1, ansi("bold", "-no-init")) RS\ +ins(2, "Do not load any initialization script.") RS\ +RS "See the man page " Command "(1) for more information." +} +function showMan( temp) { +if (ENVIRON["TRANS_MANPAGE"]) { +initPager() +Groff = detectProgram("groff", "--version") +if (Pager && Groff) { +temp = "echo -E \"${TRANS_MANPAGE}\"" +temp = temp PIPE\ +Groff " -Wall -mtty-char -mandoc -Tutf8 "\ +"-rLL=" Option["width"] "n -rLT=" Option["width"] "n" +switch (Pager) { +case "less": +temp = temp PIPE\ +Pager " -s -P\"\\ \\Manual page " Command "(1) line %lt (press h for help or q to quit)\"" +break +case "most": +temp = temp PIPE Pager " -Cs" +break +default: +temp = temp PIPE Pager +} +system(temp) +return +} +} +if (fileExists(ENVIRON["TRANS_DIR"] "/man/" Command ".1")) +system("man " parameterize(ENVIRON["TRANS_DIR"] "/man/" Command ".1") SUPERR) +else if (system("man " Command SUPERR)) +print getHelp() +} +function getReference(displayName, +code, col, cols, i, j, name, num, r, rows, saveSortedIn, +t1, t2) { +num = 0 +for (code in Locale) +if (Locale[code]["support"] != "unstable") +num++ +rows = int(num / 3) + (num % 3 ? 1 : 0) +cols[0][0] = cols[1][0] = cols[2][0] = NULLSTR +i = 0 +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = displayName == "endonym" ? "@ind_num_asc" : +"compName" +for (code in Locale) { +if (Locale[code]["support"] != "unstable") { +col = int(i / rows) +append(cols[col], code) +i++ +} +} +PROCINFO["sorted_in"] = saveSortedIn +if (displayName == "endonym") { +r = "┌" replicate("─", 23) "┬" replicate("─", 23) "┬" replicate("─", 23) "┐" RS +for (i = 0; i < rows; i++) { +r = r "│" +for (j = 0; j < 3; j++) { +if (cols[j][i]) { +t1 = getDisplay(cols[j][i]) +switch (cols[j][i]) { +case "he": +t1 = sprintf(" %-18s", t1) +break +case "ur": +t1 = sprintf(" %-17s", t1) +break +case "hi": case "gu": case "km": case "kn": +case "my": case "ne": case "pa": case "si": +case "ta": case "te": case "yi": +t1 = sprintf(" %-16s", t1) +break +case "yue": +t1 = sprintf(" %-13s", t1) +break +case "ja": case "ko": +t1 = sprintf(" %-12s", t1) +break +case "zh-CN": case "zh-TW": +t1 = sprintf(" %-11s", t1) +break +default: +if (length(t1) <= 15) +t1 = sprintf(" %-15s", t1) +} +switch (length(cols[j][i])) { +case 1: case 2: case 3: case 4: +t2 = sprintf("- %s │", ansi("bold", sprintf("%4s", cols[j][i]))) +break +case 5: +t2 = sprintf("- %s│", ansi("bold", cols[j][i])) +break +case 6: +t2 = sprintf("-%s│", ansi("bold", cols[j][i])) +break +case 7: +t2 = sprintf("-%s", ansi("bold", cols[j][i])) +break +default: +t2 = ansi("bold", cols[j][i]) +} +r = r t1 t2 +} else +r = r sprintf("%23s│", NULLSTR) +} +r = r RS +} +r = r "└" replicate("─", 23) "┴" replicate("─", 23) "┴" replicate("─", 23) "┘" +} else { +r = "┌" replicate("─", 23) "┬" replicate("─", 23) "┬" replicate("─", 23) "┐" RS +for (i = 0; i < rows; i++) { +r = r "│" +for (j = 0; j < 3; j++) { +if (cols[j][i]) { +t1 = getName(cols[j][i]) +if (length(t1) > 15) +t1 = substr(t1, 1, 12) "..." +t1 = sprintf(" %-15s", t1) +switch (length(cols[j][i])) { +case 1: case 2: case 3: case 4: +t2 = sprintf("- %s │", ansi("bold", sprintf("%4s", cols[j][i]))) +break +case 5: +t2 = sprintf("- %s│", ansi("bold", cols[j][i])) +break +case 6: +t2 = sprintf("-%s│", ansi("bold", cols[j][i])) +break +case 7: +t2 = sprintf("-%s", ansi("bold", cols[j][i])) +break +default: +t2 = ansi("bold", cols[j][i]) +} +r = r t1 t2 +} else +r = r sprintf("%23s│", NULLSTR) +} +r = r RS +} +r = r "└" replicate("─", 23) "┴" replicate("─", 23) "┴" replicate("─", 23) "┘" +} +return r +} +function getList(codes, code, i, r, saveSortedIn) { +r = NULLSTR +if (!isarray(codes)) +r = getDetails(codes) +else if (anything(codes)) { +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "@ind_num_asc" +for (i in codes) +r = (r ? r RS prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])) RS\ +: r) getDetails(codes[i]) +PROCINFO["sorted_in"] = saveSortedIn +} else +r = getDetails(Option["hl"]) +return r +} +function tokenize(returnTokens, string, +delimiters, +newlines, +quotes, +escapeChars, +leftBlockComments, +rightBlockComments, +lineComments, +reservedOperators, +reservedPatterns, +blockCommenting, +c, +currentToken, +escaping, +i, +lineCommenting, +p, +quoting, +r, +s, +tempGroup, +tempPattern, +tempString) { +if (!delimiters[0]) { +delimiters[0] = " " +delimiters[1] = "\t" +delimiters[2] = "\v" +} +if (!newlines[0]) { +newlines[0] = "\n" +newlines[1] = "\r" +} +if (!quotes[0]) { +quotes[0] = "\"" +} +if (!escapeChars[0]) { +escapeChars[0] = "\\" +} +if (!leftBlockComments[0]) { +leftBlockComments[0] = "#|" +leftBlockComments[1] = "/*" +leftBlockComments[2] = "(*" +} +if (!rightBlockComments[0]) { +rightBlockComments[0] = "|#" +rightBlockComments[1] = "*/" +rightBlockComments[2] = "*)" +} +if (!lineComments[0]) { +lineComments[0] = ";" +lineComments[1] = "//" +lineComments[2] = "#" +} +if (!reservedOperators[0]) { +reservedOperators[0] = "(" +reservedOperators[1] = ")" +reservedOperators[2] = "[" +reservedOperators[3] = "]" +reservedOperators[4] = "{" +reservedOperators[5] = "}" +reservedOperators[6] = "," +} +if (!reservedPatterns[0]) { +reservedPatterns[0] = "[+-]?((0|[1-9][0-9]*)|[.][0-9]*|(0|[1-9][0-9]*)[.][0-9]*)([Ee][+-]?[0-9]+)?" +reservedPatterns[1] = "[+-]?0[0-7]+([.][0-7]*)?" +reservedPatterns[2] = "[+-]?0[Xx][0-9A-Fa-f]+([.][0-9A-Fa-f]*)?" +} +split(string, s, "") +currentToken = "" +quoting = escaping = blockCommenting = lineCommenting = 0 +p = 0 +i = 1 +while (i <= length(s)) { +c = s[i] +r = substr(string, i) +if (blockCommenting) { +if (tempString = startsWithAny(r, rightBlockComments)) +blockCommenting = 0 +i++ +} else if (lineCommenting) { +if (belongsTo(c, newlines)) +lineCommenting = 0 +i++ +} else if (quoting) { +currentToken = currentToken c +if (escaping) { +escaping = 0 +} else { +if (belongsTo(c, quotes)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +quoting = 0 +} else if (belongsTo(c, escapeChars)) { +escaping = 1 +} else { +} +} +i++ +} else { +if (belongsTo(c, delimiters) || belongsTo(c, newlines)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +i++ +} else if (belongsTo(c, quotes)) { +if (currentToken) { +returnTokens[p++] = currentToken +} +currentToken = c +quoting = 1 +i++ +} else if (tempString = startsWithAny(r, leftBlockComments)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +blockCommenting = 1 +i += length(tempString) +} else if (tempString = startsWithAny(r, lineComments)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +lineCommenting = 1 +i += length(tempString) +} else if (tempString = startsWithAny(r, reservedOperators)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +returnTokens[p++] = tempString +i += length(tempString) +} else if (tempPattern = matchesAny(r, reservedPatterns)) { +if (currentToken) { +returnTokens[p++] = currentToken +currentToken = "" +} +match(r, "^" tempPattern, tempGroup) +returnTokens[p++] = tempGroup[0] +i += length(tempGroup[0]) +} else { +currentToken = currentToken c +i++ +} +} +} +if (currentToken) +returnTokens[p++] = currentToken +} +function parseJsonArray(returnAST, tokens, +leftBrackets, +rightBrackets, +separators, +i, j, key, p, stack, token) { +if (!leftBrackets[0]) { +leftBrackets[0] = "(" +leftBrackets[1] = "[" +leftBrackets[2] = "{" +} +if (!rightBrackets[0]) { +rightBrackets[0] = ")" +rightBrackets[1] = "]" +rightBrackets[2] = "}" +} +if (!separators[0]) { +separators[0] = "," +} +stack[p = 0] = 0 +for (i = 0; i < length(tokens); i++) { +token = tokens[i] +if (belongsTo(token, leftBrackets)) +stack[++p] = 0 +else if (belongsTo(token, rightBrackets)) +--p +else if (belongsTo(token, separators)) +stack[p]++ +else { +key = stack[0] +for (j = 1; j <= p; j++) +key = key SUBSEP stack[j] +returnAST[key] = token +} +} +} +function parseJson(returnAST, tokens, +arrayStartTokens, arrayEndTokens, +objectStartTokens, objectEndTokens, +commas, colons, +flag, i, j, key, name, p, stack, token) { +if (!arrayStartTokens[0]) arrayStartTokens[0] = "[" +if (!arrayEndTokens[0]) arrayEndTokens[0] = "]" +if (!objectStartTokens[0]) objectStartTokens[0] = "{" +if (!objectEndTokens[0]) objectEndTokens[0] = "}" +if (!commas[0]) commas[0] = "," +if (!colons[0]) colons[0] = ":" +stack[p = 0] = 0 +flag = 0 +for (i = 0; i < length(tokens); i++) { +token = tokens[i] +if (belongsTo(token, arrayStartTokens)) { +stack[++p] = 0 +} else if (belongsTo(token, objectStartTokens)) { +stack[++p] = NULLSTR +flag = 0 +} else if (belongsTo(token, objectEndTokens) || +belongsTo(token, arrayEndTokens)) { +--p +} else if (belongsTo(token, commas)) { +if (isnum(stack[p])) +stack[p]++ +else +flag = 0 +} else if (belongsTo(token, colons)) { +flag = 1 +} else if (isnum(stack[p]) || flag) { +key = stack[0] +for (j = 1; j <= p; j++) +key = key SUBSEP stack[j] +returnAST[key] = token +flag = 0 +} else { +stack[p] = unparameterize(token) +} +} +} +function parseList(returnAST, tokens, +leftBrackets, +rightBrackets, +separators, +i, j, key, p, stack, token) { +if (!leftBrackets[0]) { +leftBrackets[0] = "(" +leftBrackets[1] = "[" +leftBrackets[2] = "{" +} +if (!rightBrackets[0]) { +rightBrackets[0] = ")" +rightBrackets[1] = "]" +rightBrackets[2] = "}" +} +if (!separators[0]) { +separators[0] = "," +} +stack[p = 0] = 0 +for (i = 0; i < length(tokens); i++) { +token = tokens[i] +if (belongsTo(token, leftBrackets)) { +stack[++p] = 0 +} else if (belongsTo(token, rightBrackets)) { +stack[--p]++ +} else if (belongsTo(token, separators)) { +} else { +key = NULLSTR +if (p > 0) { +for (j = 0; j < p - 1; j++) +key = key SUBSEP stack[j] +returnAST[key][stack[p - 1]] = NULLSTR +key = key SUBSEP stack[p - 1] +} +returnAST[key][stack[p]] = token +stack[p]++ +} +} +} +function prettify(name, string, i, temp) { +temp = string +if ("sgr-" name in Option) +if (isarray(Option["sgr-" name])) +for (i in Option["sgr-" name]) +temp = ansi(Option["sgr-" name][i], temp) +else +temp = ansi(Option["sgr-" name], temp) +return temp +} +function randomColor( i) { +i = int(5 * rand()) +switch (i) { +case 0: return "green" +case 1: return "yellow" +case 2: return "blue" +case 3: return "magenta" +case 4: return "cyan" +default: return "default" +} +} +function setRandomTheme( i, n, temp) { +srand(systime()) +for (i = 0; i < 3; i++) { +do temp = randomColor(); while (belongsTo(temp, n)) +n[i] = temp +} +Option["sgr-prompt-message"] = Option["sgr-languages"] = n[0] +Option["sgr-original-dictionary-detailed-word-class"][1] = n[0] +Option["sgr-original-dictionary-detailed-word-class"][2] = "bold" +Option["sgr-original-dictionary-synonyms"] = n[0] +Option["sgr-original-dictionary-synonyms-word-class"][1] = n[0] +Option["sgr-original-dictionary-synonyms-word-class"][2] = "bold" +Option["sgr-original-dictionary-examples"] = n[0] +Option["sgr-original-dictionary-see-also"] = n[0] +Option["sgr-dictionary-word-class"][1] = n[0] +Option["sgr-dictionary-word-class"][2] = "bold" +Option["sgr-original"][1] = Option["sgr-original-phonetics"][1] = n[1] +Option["sgr-original"][2] = Option["sgr-original-phonetics"][2] = "bold" +Option["sgr-prompt-message-original"][1] = n[1] +Option["sgr-prompt-message-original"][2] = "bold" +Option["sgr-languages-sl"] = n[1] +Option["sgr-original-dictionary-detailed-explanation"][1] = n[1] +Option["sgr-original-dictionary-detailed-explanation"][2] = "bold" +Option["sgr-original-dictionary-detailed-example"] = n[1] +Option["sgr-original-dictionary-detailed-synonyms"] = n[1] +Option["sgr-original-dictionary-detailed-synonyms-item"][1] = n[1] +Option["sgr-original-dictionary-detailed-synonyms-item"][2] = "bold" +Option["sgr-original-dictionary-synonyms-synonyms"] = n[1] +Option["sgr-original-dictionary-synonyms-synonyms-item"][1] = n[1] +Option["sgr-original-dictionary-synonyms-synonyms-item"][2] = "bold" +Option["sgr-original-dictionary-examples-example"] = n[1] +Option["sgr-original-dictionary-examples-original"][1] = n[1] +Option["sgr-original-dictionary-examples-original"][2] = "bold" +Option["sgr-original-dictionary-examples-original"][3] = "underline" +Option["sgr-original-dictionary-see-also-phrases"] = n[1] +Option["sgr-original-dictionary-see-also-phrases-item"][1] = n[1] +Option["sgr-original-dictionary-see-also-phrases-item"][2] = "bold" +Option["sgr-dictionary-explanation"] = n[1] +Option["sgr-dictionary-explanation-item"][1] = n[1] +Option["sgr-dictionary-explanation-item"][2] = "bold" +Option["sgr-alternatives-original"][1] = n[1] +Option["sgr-alternatives-original"][2] = "bold" +Option["sgr-translation"][1] = Option["sgr-translation-phonetics"][1] = n[2] +Option["sgr-translation"][2] = Option["sgr-translation-phonetics"][2] = "bold" +Option["sgr-languages-tl"] = n[2] +Option["sgr-dictionary-word"][1] = n[2] +Option["sgr-dictionary-word"][2] = "bold" +Option["sgr-alternatives-translations"] = n[2] +Option["sgr-alternatives-translations-item"][1] = n[2] +Option["sgr-alternatives-translations-item"][2] = "bold" +Option["sgr-brief-translation"][1] = Option["sgr-brief-translation-phonetics"][1] = n[2] +Option["sgr-brief-translation"][2] = Option["sgr-brief-translation-phonetics"][2] = "bold" +Option["fmt-welcome-message"] = Name +Option["sgr-welcome-message"][1] = n[0] +Option["sgr-welcome-message"][2] = "bold" +Option["fmt-welcome-submessage"] = "(:q to quit)" +Option["sgr-welcome-submessage"] = n[0] +Option["fmt-prompt"] = "%s> " +Option["sgr-prompt"][1] = n[1] +Option["sgr-prompt"][2] = "bold" +} +function setDefaultTheme() { +Option["sgr-translation"] = Option["sgr-translation-phonetics"] = "bold" +Option["sgr-prompt-message-original"] = "underline" +Option["sgr-languages-sl"] = "underline" +Option["sgr-languages-tl"] = "bold" +Option["sgr-original-dictionary-detailed-explanation"] = "bold" +Option["sgr-original-dictionary-detailed-synonyms-item"] = "bold" +Option["sgr-original-dictionary-synonyms-synonyms-item"] = "bold" +Option["sgr-original-dictionary-examples-original"][1] = "bold" +Option["sgr-original-dictionary-examples-original"][2] = "underline" +Option["sgr-original-dictionary-see-also-phrases-item"] = "bold" +Option["sgr-dictionary-word"] = "bold" +Option["sgr-alternatives-original"] = "underline" +Option["sgr-alternatives-translations-item"] = "bold" +Option["fmt-welcome-message"] = Name +Option["sgr-welcome-message"] = "bold" +Option["fmt-welcome-submessage"] = "(:q to quit)" +Option["fmt-prompt"] = "%s> " +Option["sgr-prompt"] = "bold" +} +function setTheme( file, line, script) { +if (Option["theme"] && Option["theme"] != "default"\ +&& Option["theme"] != "none" && Option["theme"] != "random") { +file = Option["theme"] +if (!fileExists(file)) { +file = ENVIRON["HOME"] "/.translate-shell/" Option["theme"] +if (!fileExists(file)) { +file = ENVIRON["HOME"] "/.config/translate-shell/" Option["theme"] +if (!fileExists(file)) return +} +} +} +if (file && fileExists(file)) { +script = NULLSTR +while (getline line < file) +script = script "\n" line +loadOptions(script) +} else if (Option["theme"] == "none") +; +else if (Option["theme"] == "random") +setRandomTheme() +else +setDefaultTheme() +} +function provides(engineName) { +Translator[tolower(engineName)] = TRUE +} +function engineMethod(methodName, engine, translator) { +if (!Translator[Option["engine"]]) { +engine = tolower(Option["engine"]) +if (!Translator[engine]) +for (translator in Translator) +if (Translator[translator] && +translator ~ "^"engine) { +engine = translator +break +} +if (!Translator[engine]) { +e("[ERROR] Translator not found: " Option["engine"] "\n"\ +" Run '-list-engines / -S' to see a list of available engines.") +exit 1 +} +Option["engine"] = engine +} +return Option["engine"] methodName +} +function initAudioPlayer() { +AudioPlayer = !system("mpv" SUPOUT SUPERR) ? +"mpv --no-config" : +(!system("mplayer" SUPOUT SUPERR) ? +"mplayer" : +(!system("mpg123 --version" SUPOUT SUPERR) ? +"mpg123" : +"")) +} +function initSpeechSynthesizer() { +SpeechSynthesizer = !system("say ''" SUPOUT SUPERR) ? +"say" : +(!system("espeak ''" SUPOUT SUPERR) ? +"espeak" : +"") +} +function initPager() { +Pager = !system("less -V" SUPOUT SUPERR) ? +"less" : +(!system("more -V" SUPOUT SUPERR) ? +"more" : +(!system("most" SUPOUT SUPERR) ? +"most" : +"")) +} +function initHttpService( inet) { +_Init() +inet = "inet" +if (Option["ip-version"]) +inet = inet Option["ip-version"] +if (Option["proxy"]) { +match(Option["proxy"], /^(http:\/*)?(([^:]+):([^@]+)@)?([^\/]*):([^\/:]*)/, HttpProxySpec) +HttpAuthUser = HttpProxySpec[3] +HttpAuthPass = HttpProxySpec[4] +HttpAuthCredentials = base64(unquote(HttpAuthUser) ":" HttpAuthPass) +HttpService = "/" inet "/tcp/0/" HttpProxySpec[5] "/" HttpProxySpec[6] +HttpPathPrefix = HttpProtocol HttpHost +} else { +HttpService = "/" inet "/tcp/0/" HttpHost "/" HttpPort +HttpPathPrefix = "" +} +PROCINFO[HttpService, "READ_TIMEOUT"] = 2000 +} +function preprocess(text) { +return quote(text) +} +function preprocessByDump(text, arr, len, temp) { +len = dumpX(text, arr) +temp = "" +for (i = 1; i <= len; i++) +temp = temp "%" arr[i] +return temp +} +function postprocess(text) { +text = gensub(/ ([.,;:?!"])/, "\\1", "g", text) +text = gensub(/(["]) /, "\\1", "g", text) +return text +} +function getResponse(text, sl, tl, hl, +content, header, isBody, url, group, status, location) { +url = _RequestUrl(text, sl, tl, hl) +header = "GET " url " HTTP/1.1\r\n"\ +"Host: " HttpHost "\r\n"\ +"Connection: close\r\n" +if (Option["user-agent"]) +header = header "User-Agent: " Option["user-agent"] "\r\n" +if (Cookie) +header = header "Cookie: " Cookie "\r\n" +if (HttpAuthUser && HttpAuthPass) +header = header "Proxy-Authorization: Basic " HttpAuthCredentials "\r\n" +content = NULLSTR; isBody = 0 +while (1) { +print header |& HttpService +while ((HttpService |& getline) > 0) { +if (isBody) +content = content ? content "\n" $0 : $0 +else if (length($0) <= 1) +isBody = 1 +else { +match($0, /^HTTP[^ ]* ([^ ]*)/, group) +if (RSTART) status = group[1] +match($0, /^Location: (.*)/, group) +if (RSTART) location = squeeze(group[1]) +} +l(sprintf("%4s bytes > %s", length($0), $0)) +} +close(HttpService) +if (ERRNO == "Connection timed out") { +w("[WARNING] " ERRNO ". Retrying IPv4 connection.") +Option["ip-version"] = 4 +initHttpService() +PROCINFO[HttpService, "READ_TIMEOUT"] = 0 +ERRNO = "" +} else +break +} +if ((status == "301" || status == "302") && location) +content = curl(location) +return assert(content, "[ERROR] Null response.") +} +function postResponse(text, sl, tl, hl, type, +content, contentLength, contentType, group, +header, isBody, reqBody, url, status, location, userAgent) { +url = _PostRequestUrl(text, sl, tl, hl, type) +contentType = _PostRequestContentType(text, sl, tl, hl, type) +userAgent = _PostRequestUserAgent(text, sl, tl, hl, type) +reqBody = _PostRequestBody(text, sl, tl, hl, type) +if (DumpContentengths[reqBody]) +contentLength = DumpContentengths[reqBody] +else +contentLength = DumpContentengths[reqBody] = dump(reqBody, group) +header = "POST " url " HTTP/1.1\r\n"\ +"Host: " HttpHost "\r\n"\ +"Connection: close\r\n"\ +"Content-Length: " contentLength "\r\n"\ +"Content-Type: " contentType "\r\n" +if (Option["user-agent"] && !userAgent) +header = header "User-Agent: " Option["user-agent"] "\r\n" +if (userAgent) +header = header "User-Agent: " userAgent "\r\n" +if (Cookie) +header = header "Cookie: " Cookie "\r\n" +if (HttpAuthUser && HttpAuthPass) +header = header "Proxy-Authorization: Basic " HttpAuthCredentials "\r\n" +content = NULLSTR; isBody = 0 +while (1) { +print (header "\r\n" reqBody) |& HttpService +while ((HttpService |& getline) > 0) { +if (isBody) +content = content ? content "\n" $0 : $0 +else if (length($0) <= 1) +isBody = 1 +else { +match($0, /^HTTP[^ ]* ([^ ]*)/, group) +if (RSTART) status = group[1] +match($0, /^Location: (.*)/, group) +if (RSTART) location = squeeze(group[1]) +} +l(sprintf("%4s bytes > %s", length($0), $0)) +} +close(HttpService) +if (ERRNO == "Connection timed out") { +w("[WARNING] " ERRNO ". Retrying IPv4 connection.") +Option["ip-version"] = 4 +initHttpService() +PROCINFO[HttpService, "READ_TIMEOUT"] = 0 +ERRNO = "" +} else +break +} +if (status == "404") { +e("[ERROR] 404 Not Found") +exit 1 +} +if ((status == "301" || status == "302") && location) { +url = "https" substr(url, 5) +content = curlPost(url, reqBody) +} +return content +} +function p(string) { +if (Option["view"]) { +print string | Option["pager"] +close(Option["pager"]) +} else +print string > Option["output"] +} +function play(text, tl, url, status) { +url = _TTSUrl(text, tl) +status = system(Option["player"] " " parameterize(url) SUPOUT SUPERR) +if (status) +w("Voice output isn't available for " getName(tl)) +return status +} +function download_audio(text, tl, url, output) { +url = _TTSUrl(text, tl) +if (Option["download-audio-as"]) +output = Option["download-audio-as"] +else +output = text " [" Option["engine"] "] (" Option["narrator"] ").ts" +if (url ~ /^\//) +system("mv -- " parameterize(url) " " parameterize(output)) +else +curl(url, output) +} +function getTranslation(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl) { +return _Translate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl) +} +function fileTranslation(uri, group, temp1, temp2) { +temp1 = Option["input"] +temp2 = Option["verbose"] +match(uri, /^file:\/\/(.*)/, group) +Option["input"] = group[1] +Option["verbose"] = 0 +translateMain() +Option["input"] = temp1 +Option["verbose"] = temp2 +} +function webTranslation(uri, sl, tl, hl, temp) { +temp = _WebTranslateUrl(uri, sl, tl, hl) +if (temp) { +p(temp) +if (Option["browser"] != NONE) +system(Option["browser"] " " parameterize(temp) SUPOUT SUPERR) +} +} +function translate(text, inline, +i, j, playlist, il, saveSortedIn) { +if (!getCode(Option["hl"])) { +w("[WARNING] Unknown language code: " Option["hl"] ", fallback to English: en") +Option["hl"] = "en" +} else if (isRTL(Option["hl"])) { +if (!FriBidi) +w("[WARNING] " getName(Option["hl"]) " is a right-to-left language, but FriBidi is not found.") +} +if (!getCode(Option["sl"])) { +w("[WARNING] Unknown source language code: " Option["sl"]) +} else if (isRTL(Option["sl"])) { +if (!FriBidi) +w("[WARNING] " getName(Option["sl"]) " is a right-to-left language, but FriBidi is not found.") +} +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "@ind_num_asc" +for (i in Option["tl"]) { +if (!Option["interactive"]) +if (Option["verbose"] && i > 1) +p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"]))) +if (inline && +startsWithAny(text, UriSchemes) == "file://") { +fileTranslation(text) +} else if (inline && +startsWithAny(text, UriSchemes) == "http://" || +startsWithAny(text, UriSchemes) == "https://") { +webTranslation(text, Option["sl"], Option["tl"][i], Option["hl"]) +} else { +if (!Option["no-translate"]) +p(getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"] || Option["download-audio"], playlist, il)) +else +il[0] = Option["sl"] == "auto" ? "en" : Option["sl"] +if (Option["play"] == 1) { +if (Option["player"]) +for (j in playlist) +play(playlist[j]["text"], playlist[j]["tl"]) +else if (SpeechSynthesizer) +for (j in playlist) +print playlist[j]["text"] | SpeechSynthesizer +} else if (Option["play"] == 2) { +if (Option["player"]) +play(text, il[0]) +else if (SpeechSynthesizer) +print text | SpeechSynthesizer +} +if (Option["download-audio"] == 1) { +if (Option["play"] != 2 && !Option["no-translate"]) +download_audio(playlist[length(playlist) - 1]["text"],\ +playlist[length(playlist) - 1]["tl"]) +else +download_audio(text, il[0]) +} +} +} +PROCINFO["sorted_in"] = saveSortedIn +} +function translates(text, inline, +i) { +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "@ind_num_asc" +for (i in Option["sls"]) { +if (!Option["interactive"]) +if (Option["verbose"] && i > 1) +p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"]))) +Option["sl"] = Option["sls"][i] +translate(text, inline) +} +PROCINFO["sorted_in"] = saveSortedIn +} +function translateMain( i, line) { +if (Option["interactive"]) +prompt() +if (Option["input"] == STDIN || fileExists(Option["input"])) { +i = 0 +while (getline line < Option["input"]) +if (line) { +if (!Option["interactive"]) +if (Option["verbose"] && i++ > 0) +p(prettify("source-seperator", +replicate(Option["chr-source-seperator"], +Option["width"]))) +if (Option["interactive"]) +repl(line) +else +translates(line) +} else { +if (!Option["interactive"]) +if (!Option["verbose"]) +p(line) +} +} else +e("[ERROR] File not found: " Option["input"]) +} +function _Init( vm) { +vm = engineMethod("Init") +return @vm() +} +function _RequestUrl(text, sl, tl, hl, vm) { +vm = engineMethod("RequestUrl") +return @vm(text, sl, tl, hl) +} +function _PostRequestUrl(text, sl, tl, hl, type, vm) { +vm = engineMethod("PostRequestUrl") +return @vm(text, sl, tl, hl, type) +} +function _PostRequestContentType(text, sl, tl, hl, type, vm) { +vm = engineMethod("PostRequestContentType") +return @vm(text, sl, tl, hl, type) +} +function _PostRequestUserAgent(text, sl, tl, hl, type, vm) { +vm = engineMethod("PostRequestUserAgent") +return @vm(text, sl, tl, hl, type) +} +function _PostRequestBody(text, sl, tl, hl, type, vm) { +vm = engineMethod("PostRequestBody") +return @vm(text, sl, tl, hl, type) +} +function _TTSUrl(text, tl, vm) { +vm = engineMethod("TTSUrl") +return @vm(text, tl) +} +function _WebTranslateUrl(uri, sl, tl, hl, vm) { +vm = engineMethod("WebTranslateUrl") +return @vm(uri, sl, tl, hl) +} +function _Translate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +vm) { +vm = engineMethod("Translate") +return @vm(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl) +} +BEGIN { provides("google") } +function genRL(a, x, +b, c, d, i, y) { +tokenize(y, x) +parseList(b, y) +i = SUBSEP 0 +for (c = 0; c < length(b[i]) - 2; c += 3) { +d = b[i][c + 2] +d = d >= 97 ? d - 87 : +d - 48 +d = b[i][c + 1] == 43 ? rshift(a, d) : lshift(a, d) +a = b[i][c] == 43 ? and(a + d, 4294967295) : xor(a, d) +} +return a +} +function genTK(text, +a, d, dLen, e, tkk, ub, vb) { +if (TK[text]) return TK[text] +tkk = systime() / 3600 +ub = "[43,45,51,94,43,98,43,45,102]" +vb = "[43,45,97,94,43,54]" +dLen = dump(text, d) +a = tkk +for (e = 1; e <= dLen; e++) +a = genRL(a + d[e], vb) +a = genRL(a, ub) +0 > a && (a = and(a, 2147483647) + 2147483648) +a %= 1e6 +TK[text] = a "." xor(a, tkk) +l(text, "text") +l(tkk, "tkk") +l(TK[text], "tk") +return TK[text] +} +function googleInit() { +HttpProtocol = "http://" +HttpHost = "translate.googleapis.com" +HttpPort = 80 +} +function googleRequestUrl(text, sl, tl, hl, qc) { +qc = Option["no-autocorrect"] ? "qc" : "qca"; +return HttpPathPrefix "/translate_a/single?client=gtx"\ +"&ie=UTF-8&oe=UTF-8"\ +"&dt=bd&dt=ex&dt=ld&dt=md&dt=rw&dt=rm&dt=ss&dt=t&dt=at&dt=gt"\ +"&dt=" qc "&sl=" sl "&tl=" tl "&hl=" hl\ +"&q=" preprocessByDump(text) +} +function googleTTSUrl(text, tl) { +return HttpProtocol HttpHost "/translate_tts?ie=UTF-8&client=gtx"\ +"&tl=" tl "&q=" preprocessByDump(text) +} +function googleWebTranslateUrl(uri, sl, tl, hl) { +return "https://translate.google.com/translate?"\ +"hl=" hl "&sl=" sl "&tl=" tl "&u=" uri +} +function googleTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +r, +content, tokens, ast, +_sl, _tl, _hl, il, ils, isPhonetic, +article, example, explanation, ref, word, +translation, translations, phonetics, +wordClasses, words, segments, altTranslations, +original, oPhonetics, oWordClasses, oWords, +oRefs, oSynonymClasses, oSynonyms, +oExamples, oSeeAlso, +wShowOriginal, wShowOriginalPhonetics, +wShowTranslation, wShowTranslationPhonetics, +wShowPromptMessage, wShowLanguages, +wShowOriginalDictionary, wShowDictionary, +wShowAlternatives, +genderedTrans, hasWordClasses, hasAltTranslations, +i, j, k, group, temp, saveSortedIn) { +isPhonetic = match(tl, /^@/) +tl = substr(tl, 1 + isPhonetic) +if (!getCode(tl)) { +w("[WARNING] Unknown target language code: " tl) +} else if (isRTL(tl)) { +if (!FriBidi) +w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.") +} +_sl = getCode(sl); if (!_sl) _sl = sl +_tl = getCode(tl); if (!_tl) _tl = tl +_hl = getCode(hl); if (!_hl) _hl = hl +content = getResponse(text, _sl, _tl, _hl) +if (Option["dump"]) +return content +tokenize(tokens, content) +parseJsonArray(ast, tokens) +l(content, "content", 1, 1) +l(tokens, "tokens", 1, 0, 1) +l(ast, "ast") +if (!isarray(ast) || !anything(ast)) { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +ExitCode = 1 +return +} +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "compareByIndexFields" +for (i in ast) { +if (ast[i] == "null") continue +if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "0$") +append(translations, literal(ast[i])) +if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "1$") +append(original, literal(ast[i])) +if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "2$") +append(phonetics, literal(ast[i])) +if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "3$") +append(oPhonetics, literal(ast[i])) +if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +wordClasses[group[1]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "([[:digit:]]+)$", group)) +words[group[1]][group[2]][group[3]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)$", group)) +words[group[1]][group[2]]["1"][group[3]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) { +segments[group[1]] = literal(ast[i]) +altTranslations[group[1]][0] = "" +} +if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +altTranslations[group[1]][group[2]] = literal(ast[i]) +if (i ~ "^0" SUBSEP "7" SUBSEP "5$") { +if (ast[i] == "true") +w("Showing translation for: (use -no-auto to disable autocorrect)") +else +w("Did you mean: "\ +ansi("bold", unparameterize(ast["0" SUBSEP "7" SUBSEP "1"]))) +} +if (i ~ "^0" SUBSEP "8" SUBSEP "0" SUBSEP "[[:digit:]]+$" || +i ~ "^0" SUBSEP "2$") +append(ils, literal(ast[i])) +if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +oSynonymClasses[group[1]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group)) +if (ast[i]) { +oRefs[literal(ast[i])][1] = group[1] +oRefs[literal(ast[i])][2] = group[2] +} +if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group)) +oSynonyms[group[1]][group[2]][group[3]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +oWordClasses[group[1]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +oWords[group[1]][group[2]][0] = literal(ast[i]) +if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group)) +oWords[group[1]][group[2]][1] = literal(ast[i]) +if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2$", group)) +oWords[group[1]][group[2]][2] = literal(ast[i]) +if (match(i, "^0" SUBSEP "13" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) +oExamples[group[1]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "14" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group)) +oSeeAlso[group[1]] = literal(ast[i]) +if (match(i, "^0" SUBSEP "18" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group)) +genderedTrans[group[1]] = literal(ast[i]) +} +PROCINFO["sorted_in"] = saveSortedIn +translation = join(translations) +returnIl[0] = il = !anything(ils) || belongsTo(sl, ils) ? sl : ils[0] +if (Option["verbose"] < -1) +return il +else if (Option["verbose"] < 0) +return getList(il) +if (!isVerbose) { +r = isPhonetic && anything(phonetics) ? +prettify("brief-translation-phonetics", join(phonetics, " ")) : +prettify("brief-translation", s(translation, tl)) +if (toSpeech) { +returnPlaylist[0]["text"] = translation +returnPlaylist[0]["tl"] = _tl +} +} else { +wShowOriginal = Option["show-original"] +wShowOriginalPhonetics = Option["show-original-phonetics"] +wShowTranslation = Option["show-translation"] +wShowTranslationPhonetics = Option["show-translation-phonetics"] +wShowPromptMessage = Option["show-prompt-message"] +wShowLanguages = Option["show-languages"] +wShowOriginalDictionary = Option["show-original-dictionary"] +wShowDictionary = Option["show-dictionary"] +wShowAlternatives = Option["show-alternatives"] +if (!anything(oPhonetics)) wShowOriginalPhonetics = 0 +if (!anything(phonetics)) wShowTranslationPhonetics = 0 +if (getCode(il) == getCode(tl) &&\ +(isarray(oWordClasses) || isarray(oSynonymClasses) ||\ +isarray(oExamples) || isarray(oSeeAlso))) { +wShowOriginalDictionary = 1 +wShowTranslation = 0 +} +hasWordClasses = exists(wordClasses) +hasAltTranslations = exists(altTranslations[0]) +if (!hasWordClasses && !hasAltTranslations) +wShowPromptMessage = wShowLanguages = 0 +if (!hasWordClasses) wShowDictionary = 0 +if (!hasAltTranslations) wShowAlternatives = 0 +if (wShowOriginal) { +if (r) r = r RS RS +r = r m("-- display original text & phonetics") +r = r prettify("original", s(join(original, " "), il)) +if (wShowOriginalPhonetics) +r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), il)) +} +if (wShowTranslation) { +if (r) r = r RS RS +r = r m("-- display major translation & phonetics") +if (!exists(genderedTrans)) +r = r prettify("translation", s(translation, tl)) +else { +r = r prettify("prompt-message", s("(♂) ", hl)) +r = r prettify("translation", s(genderedTrans[0], tl)) RS +r = r prettify("prompt-message", s("(♀) ", hl)) +r = r prettify("translation", s(genderedTrans[1], tl)) +} +if (wShowTranslationPhonetics) +r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl)) +} +if (wShowPromptMessage || wShowLanguages) +if (r) r = r RS +if (wShowPromptMessage) { +if (hasWordClasses) { +if (r) r = r RS +r = r m("-- display prompt message (Definitions of ...)") +if (isRTL(hl)) +r = r prettify("prompt-message", s(showDefinitionsOf(hl, join(original, " ")))) +else { +split(showDefinitionsOf(hl, "\0%s\0"), group, "\0") +for (i = 1; i <= length(group); i++) { +if (group[i] == "%s") +r = r prettify("prompt-message-original", show(join(original, " "), il)) +else +r = r prettify("prompt-message", group[i]) +} +} +} else if (hasAltTranslations) { +if (r) r = r RS +r = r m("-- display prompt message (Translations of ...)") +if (isRTL(hl)) +r = r prettify("prompt-message", s(showTranslationsOf(hl, join(original, " ")))) +else { +split(showTranslationsOf(hl, "\0%s\0"), group, "\0") +for (i = 1; i <= length(group); i++) { +if (group[i] == "%s") +r = r prettify("prompt-message-original", show(join(original, " "), il)) +else +r = r prettify("prompt-message", group[i]) +} +} +} +} +if (wShowLanguages) { +if (r) r = r RS +r = r m("-- display source language -> target language") +temp = Option["fmt-languages"] +if (!temp) temp = "[ %s -> %t ]" +split(temp, group, /(%s|%S|%t|%T)/) +r = r prettify("languages", group[1]) +if (temp ~ /%s/) +r = r prettify("languages-sl", getDisplay(il)) +if (temp ~ /%S/) +r = r prettify("languages-sl", getName(il)) +r = r prettify("languages", group[2]) +if (temp ~ /%t/) +r = r prettify("languages-tl", getDisplay(tl)) +if (temp ~ /%T/) +r = r prettify("languages-tl", getName(tl)) +r = r prettify("languages", group[3]) +} +if (wShowOriginalDictionary) { +if (exists(oWordClasses)) { +if (r) r = r RS +r = r m("-- display original dictionary (detailed explanations)") +for (i = 0; i < length(oWordClasses); i++) { +r = (i > 0 ? r RS : r) RS prettify("original-dictionary-detailed-word-class", s(oWordClasses[i], hl)) +for (j = 0; j < length(oWords[i]); j++) { +explanation = oWords[i][j][0] +ref = oWords[i][j][1] +example = oWords[i][j][2] +r = (j > 0 ? r RS : r) RS prettify("original-dictionary-detailed-explanation", ins(1, explanation, il)) +if (example) +r = r RS prettify("original-dictionary-detailed-example", ins(2, "- \"" example "\"", il)) +if (ref && isarray(oRefs[ref])) { +temp = prettify("original-dictionary-detailed-synonyms", ins(1, show(showSynonyms(hl), hl) ": ")) +temp = temp prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0], il)) +for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++) +temp = temp prettify("original-dictionary-detailed-synonyms", ", ")\ +prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k], il)) +r = r RS temp +} +} +} +} +if (exists(oSynonymClasses)) { +r = r RS RS +r = r m("-- display original dictionary (synonyms)") +r = r prettify("original-dictionary-synonyms", s(showSynonyms(hl), hl)) +for (i = 0; i < length(oSynonymClasses); i++) { +r = (i > 0 ? r RS : r) RS prettify("original-dictionary-synonyms-word-class", ins(1, oSynonymClasses[i], hl)) +for (j = 0; j < length(oSynonyms[i]); j++) { +temp = prettify("original-dictionary-synonyms-synonyms", ins(2, "- ")) +temp = temp prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][0], il)) +for (k = 1; k < length(oSynonyms[i][j]); k++) +temp = temp prettify("original-dictionary-synonyms-synonyms", ", ")\ +prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][k], il)) +r = r RS temp +} +} +} +if (exists(oExamples)) { +r = r RS RS +r = r m("-- display original dictionary (examples)") +r = r prettify("original-dictionary-examples", s(showExamples(hl), hl)) +for (i = 0; i < length(oExamples); i++) { +example = oExamples[i] +temp = prettify("original-dictionary-examples-example", ins(1, "- ")) +split(example, group, /(|<\/b>)/) +if (group[3] ~ / [[:punct:].]/) +group[3] = substr(group[3], 2) +if (isRTL(il)) +temp = temp show(group[1] group[2] group[3], il) +else +temp = temp prettify("original-dictionary-examples-example", group[1])\ +prettify("original-dictionary-examples-original", group[2])\ +prettify("original-dictionary-examples-example", group[3]) +r = (i > 0 ? r RS : r) RS temp +} +} +if (exists(oSeeAlso)) { +r = r RS RS +r = r m("-- display original dictionary (see also)") +r = r prettify("original-dictionary-see-also", s(showSeeAlso(hl), hl)) +temp = ins(1, prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[0], il))) +for (k = 1; k < length(oSeeAlso); k++) +temp = temp prettify("original-dictionary-see-also-phrases", ", ")\ +prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[k], il)) +r = r RS temp +} +} +if (wShowDictionary) { +if (r) r = r RS +r = r m("-- display dictionary entries") +for (i = 0; i < length(wordClasses); i++) { +r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl)) +for (j = 0; j < length(words[i]); j++) { +word = words[i][j][0] +article = words[i][j][4] +if (isRTL(il)) +explanation = join(words[i][j][1], ", ") +else { +explanation = prettify("dictionary-explanation-item", words[i][j][1][0]) +for (k = 1; k < length(words[i][j][1]); k++) +explanation = explanation prettify("dictionary-explanation", ", ")\ +prettify("dictionary-explanation-item", words[i][j][1][k]) +} +r = r RS prettify("dictionary-word", ins(1, (article ? "(" article ") " : "") word, tl)) +if (isRTL(il)) +r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il)) +else +r = r RS ins(2, explanation) +} +} +} +if (wShowAlternatives) { +if (r) r = r RS RS +r = r m("-- display alternative translations") +for (i = 0; i < length(altTranslations); i++) { +r = (i > 0 ? r RS : r) prettify("alternatives-original", show(segments[i], il)) +if (isRTL(tl)) { +temp = join(altTranslations[i], ", ") +r = r RS prettify("alternatives-translations-item", ins(1, temp, tl)) +} else { +temp = prettify("alternatives-translations-item", altTranslations[i][0]) +for (j = 1; j < length(altTranslations[i]); j++) +temp = temp prettify("alternatives-translations", ", ")\ +prettify("alternatives-translations-item", altTranslations[i][j]) +r = r RS ins(1, temp) +} +} +} +if (toSpeech) { +if (index(showTranslationsOf(hl, "%s"), "%s") > 2) { +returnPlaylist[0]["text"] = showTranslationsOf(hl) +returnPlaylist[0]["tl"] = _hl +returnPlaylist[1]["text"] = join(original) +returnPlaylist[1]["tl"] = il +} else { +returnPlaylist[0]["text"] = join(original) +returnPlaylist[0]["tl"] = il +returnPlaylist[1]["text"] = showTranslationsOf(hl) +returnPlaylist[1]["tl"] = _hl +} +returnPlaylist[2]["text"] = translation +returnPlaylist[2]["tl"] = _tl +} +} +return r +} +BEGIN { provides("bing") } +function bingInit() { +HttpProtocol = "http://" +HttpHost = "www.bing.com" +HttpPort = 80 +} +function bingSetIG( content, cookie, group, header, isBody, +url, status, location) { +url = HttpPathPrefix "/translator" +header = "GET " url " HTTP/1.1\r\n"\ +"Host: " HttpHost "\r\n"\ +"Connection: close\r\n" +if (Option["user-agent"]) +header = header "User-Agent: " Option["user-agent"] "\r\n" +cookie = NULLSTR +print header |& HttpService +while ((HttpService |& getline) > 0) { +match($0, /Set-Cookie: ([^;]*);/, group) +if (group[1]) { +cookie = cookie (cookie ? "; " : NULLSTR) group[1] +} +if (isBody) +content = content ? content "\n" $0 : $0 +else if (length($0) <= 1) +isBody = 1 +else { +match($0, /^HTTP[^ ]* ([^ ]*)/, group) +if (RSTART) status = group[1] +match($0, /^Location: (.*)/, group) +if (RSTART) location = squeeze(group[1]) +} +l(sprintf("%4s bytes > %s", length($0), length($0) < 1024 ? $0 : "...")) +} +close(HttpService) +if ((status == "301" || status == "302") && location) +content = curl(location) +Cookie = cookie +match(content, /IG:"([^"]+)"/, group) +if (group[1]) { +IG = group[1] +} else { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +exit 1 +} +} +function bingTTSUrl(text, tl, +country, gender, i, group, +header, content, isBody) { +gender = "female" +country = NULLSTR +split(Option["narrator"], group, ",") +for (i in group) { +if (group[i] ~ /^(f(emale)?|w(oman)?)$/) +gender = "female" +else if (group[i] ~ /^m(ale|an)?$/) +gender = "male" +else +country = group[i] +} +if (country) tl = tl "-" country +else if (tl == "ar") tl = tl "-EG" +else if (tl == "da") tl = tl "-DK" +else if (tl == "de") tl = tl "-DE" +else if (tl == "en") tl = tl "-US" +else if (tl == "es") tl = tl "-ES" +else if (tl == "fi") tl = tl "-FI" +else if (tl == "fr") tl = tl "-FR" +else if (tl == "it") tl = tl "-IT" +else if (tl == "ja") tl = tl "-JP" +else if (tl == "ko") tl = tl "-KR" +else if (tl == "nl") tl = tl "-NL" +else if (tl == "nb") tl = tl "-NO" +else if (tl == "pl") tl = tl "-PL" +else if (tl == "pt") tl = tl "-PT" +else if (tl == "ru") tl = tl "-RU" +else if (tl == "sv") tl = tl "-SE" +else if (tl == "yue") ; +else if (tl == "zh") tl = tl "-CN" +header = "GET " "/tspeak?"\ +"&language=" tl "&text=" preprocess(text)\ +"&options=" gender "&format=audio%2Fmp3" " HTTP/1.1\r\n"\ +"Host: " HttpHost "\r\n"\ +"Connection: close\r\n" +if (Option["user-agent"]) +header = header "User-Agent: " Option["user-agent"] "\r\n" +if (Cookie) +header = header "Cookie: " Cookie "\r\n" +content = NULLSTR; isBody = 0 +print header |& HttpService +while ((HttpService |& getline) > 0) { +if (isBody) +content = content ? content "\n" $0 : $0 +else if (length($0) <= 1) +isBody = 1 +} +close(HttpService) +if (!TempFile) +TempFile = getOutput("mktemp") +printf("%s", content) > TempFile +close(TempFile) +return TempFile +} +function bingWebTranslateUrl(uri, sl, tl, hl, _sl, _tl) { +_sl = sl; _tl = tl +if (_sl == "bs") _sl = "bs-Latn" +if (_sl == "zh") _sl = "zh-CHS" +if (_sl == "zh-CN") _sl = "zh-CHS" +if (_sl == "zh-TW") _sl = "zh-CHT" +if (_tl == "bs") _tl = "bs-Latn" +if (_tl == "zh") _tl = "zh-CHS" +if (_tl == "zh-CN") _tl = "zh-CHS" +if (_tl == "zh-TW") _tl = "zh-CHT" +return "https://www.translatetheweb.com/?" "from=" _sl "&to=" _tl "&a=" uri +} +function bingRequestUrl(text, sl, tl, hl) { +return HttpPathPrefix "/translator/api/Dictionary/Lookup?"\ +"from=" sl "&to=" tl "&text=" preprocess(text) +} +function bingPostRequestUrl(text, sl, tl, hl, type) { +if (type == "lookup") +return HttpPathPrefix "/tlookupv3" +else # type == "translate" +return HttpPathPrefix "/ttranslatev3" +} +function bingPostRequestContentType(text, sl, tl, hl, type) { +return "application/x-www-form-urlencoded" +} +function bingPostRequestUserAgent(text, sl, tl, hl, type) { +return "" +} +function bingPostRequestBody(text, sl, tl, hl, type) { +if (type == "lookup") +return "&text=" quote(text) "&from=" sl "&to=" tl +else # type == "translate" +return "&text=" quote(text) "&fromLang=" sl "&to=" tl +} +function bingTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +r, +content, tokens, ast, dicContent, dicTokens, dicAst, +_sl, _tl, _hl, il, isPhonetic, +translation, phonetics, oPhonetics, +wordClasses, words, wordBackTranslations, +wShowOriginal, wShowOriginalPhonetics, +wShowTranslation, wShowTranslationPhonetics, +wShowLanguages, wShowDictionary, +i, j, k, group, temp, saveSortedIn) { +isPhonetic = match(tl, /^@/) +tl = substr(tl, 1 + isPhonetic) +if (!getCode(tl)) { +w("[WARNING] Unknown target language code: " tl) +} else if (isRTL(tl)) { +if (!FriBidi) +w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.") +} +_sl = getCode(sl); if (!_sl) _sl = sl +_tl = getCode(tl); if (!_tl) _tl = tl +_hl = getCode(hl); if (!_hl) _hl = hl +if (_sl == "auto") _sl = "auto-detect" +if (_sl == "bs") _sl = "bs-Latn" +if (_sl == "no") _sl = "nb" +if (_sl == "pt") _sl = "pt-pt" +if (_sl == "zh-CN") _sl = "zh-Hans" +if (_sl == "zh-TW") _sl = "zh-Hant" +if (_tl == "bs") _tl = "bs-Latn" +if (_tl == "no") _tl = "nb" +if (_tl == "pt") _tl = "pt-pt" +if (_tl == "zh-CN") _tl = "zh-Hans" +if (_tl == "zh-TW") _tl = "zh-Hant" +content = postResponse(text, _sl, _tl, _hl, "translate") +if (content == "") { +HttpHost = "cn.bing.com" +if (Option["proxy"]) { +HttpPathPrefix = HttpProtocol HttpHost +} else { +HttpService = "/" "inet" "/tcp/0/" HttpHost "/" HttpPort +} +content = postResponse(text, _sl, _tl, _hl, "translate") +} +if (Option["dump"]) +return content +tokenize(tokens, content) +parseJson(ast, tokens) +l(content, "content", 1, 1) +l(tokens, "tokens", 1, 0, 1) +l(ast, "ast") +if (!isarray(ast) || !anything(ast)) { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +ExitCode = 1 +return +} +translation = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0 SUBSEP "text"]) +returnIl[0] = il = _sl == "auto-detect" ? +unparameterize(ast[0 SUBSEP 0 SUBSEP "detectedLanguage" SUBSEP "language"]) : _sl +if (Option["verbose"] < -1) +return il +if (Option["verbose"] < 0) +return getList(il) +wShowTranslationPhonetics = Option["show-translation-phonetics"] +if (wShowTranslationPhonetics) { +split(_tl, group, "-") +phonetics = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0 SUBSEP "transliteration"\ +SUBSEP "text"]) +if (phonetics == translation) phonetics = "" +} +if (!isVerbose) { +r = isPhonetic && phonetics ? +prettify("brief-translation-phonetics", join(phonetics, " ")) : +prettify("brief-translation", s(translation, tl)) +} else { +wShowOriginal = Option["show-original"] +wShowTranslation = Option["show-translation"] +wShowLanguages = Option["show-languages"] +wShowDictionary = Option["show-dictionary"] +wShowOriginalPhonetics = Option["show-original-phonetics"] +if (wShowOriginalPhonetics) { +split(_sl, group, "-") +delete ast +content = postResponse(text, il, il, _hl, "translate") +tokenize(tokens, content) +parseJson(ast, tokens) +oPhonetics = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0\ +SUBSEP "transliteration" SUBSEP "text"]) +if (oPhonetics == text) oPhonetics = "" +} +if (!oPhonetics) wShowOriginalPhonetics = 0 +if (!phonetics) wShowTranslationPhonetics = 0 +if (wShowOriginal) { +if (r) r = r RS RS +r = r m("-- display original text") +r = r prettify("original", s(text, _sl)) +if (wShowOriginalPhonetics) +r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), _sl)) +} +if (wShowTranslation) { +if (r) r = r RS RS +r = r m("-- display major translation") +r = r prettify("translation", s(translation, tl)) +if (wShowTranslationPhonetics) +r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl)) +} +if (wShowLanguages) { +if (r) r = r RS RS +r = r m("-- display source language -> target language") +temp = Option["fmt-languages"] +if (!temp) temp = "[ %s -> %t ]" +split(temp, group, /(%s|%S|%t|%T)/) +r = r prettify("languages", group[1]) +if (temp ~ /%s/) +r = r prettify("languages-sl", getDisplay(il)) +if (temp ~ /%S/) +r = r prettify("languages-sl", getName(il)) +r = r prettify("languages", group[2]) +if (temp ~ /%t/) +r = r prettify("languages-tl", getDisplay(tl)) +if (temp ~ /%T/) +r = r prettify("languages-tl", getName(tl)) +r = r prettify("languages", group[3]) +} +if (wShowDictionary) { +dicContent = postResponse(text, il, _tl, _hl, "lookup") +if (dicContent != "") { +tokenize(dicTokens, dicContent) +parseJson(dicAst, dicTokens) +l(dicContent, "dicContent", 1, 1) +l(dicTokens, "dicTokens", 1, 0, 1) +l(dicAst, "dicAst") +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "compareByIndexFields" +for (i in dicAst) { +if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\ +"posTag$", group)) +wordClasses[group[1]] = tolower(literal(dicAst[i])) +} +for (i in dicAst) { +if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\ +"displayTarget$", group)) +words[wordClasses[group[1]]][group[1]] = literal(dicAst[i]) +if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\ +"backTranslations" SUBSEP "([[:digit:]]+)" SUBSEP "displayText$", group)) +wordBackTranslations[wordClasses[group[1]]][group[1]][group[2]] = literal(dicAst[i]) +} +PROCINFO["sorted_in"] = saveSortedIn +if (r) r = r RS +r = r m("-- display dictionary entries") +for (i = 0; i < length(words); i++) { +r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl)) +for (j in words[wordClasses[i]]) { +r = r RS prettify("dictionary-word", ins(1, words[wordClasses[i]][j], tl)) +if (isRTL(il)) +explanation = join(wordBackTranslations[wordClasses[i]][j], ", ") +else { +explanation = prettify("dictionary-explanation-item", +wordBackTranslations[wordClasses[i]][j][0]) +for (k = 1; k < length(wordBackTranslations[wordClasses[i]][j]); k++) +explanation = explanation prettify("dictionary-explanation", ", ")\ +prettify("dictionary-explanation-item", +wordBackTranslations[wordClasses[i]][j][k]) +} +if (isRTL(il)) +r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il)) +else +r = r RS ins(2, explanation) +} +} +} +} +} +if (toSpeech) { +returnPlaylist[0]["text"] = translation +returnPlaylist[0]["tl"] = _tl +} +return r +} +BEGIN { provides("yandex") } +function genSID( content, group, temp) { +content = curl("http://translate.yandex.com") +match(content, /SID:[[:space:]]*'([^']+)'/, group) +if (group[1]) { +split(group[1], temp, ".") +SID = reverse(temp[1]) "." reverse(temp[2]) "." reverse(temp[3]) +} else { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +exit 1 +} +} +function yandexInit() { +genSID() +YandexWebTranslate = "z5h64q92x9.net" +HttpProtocol = "http://" +HttpHost = "translate.yandex.net" +HttpPort = 80 +} +function yandexRequestUrl(text, sl, tl, hl, group) { +split(sl, group, "-"); sl = group[1] +split(tl, group, "-"); tl = group[1] +return HttpPathPrefix "/api/v1/tr.json/translate?"\ +"id=" SID "-0-0&srv=tr-text"\ +"&text=" preprocess(text) "&lang=" (sl == "auto" ? tl : sl "-" tl) +} +function yandexPostRequestBody(text, sl, tl, hl, type) { +return "text=" quote(text) "&lang=" sl +} +function yandexGetDictionaryResponse(text, sl, tl, hl, content, header, isBody, url) { +split(sl, group, "-"); sl = group[1] +split(tl, group, "-"); tl = group[1] +url = "http://dictionary.yandex.net/dicservice.json/lookupMultiple?"\ +"&text=" preprocess(text) "&dict=" sl "-" tl +content = curl(url) +return assert(content, "[ERROR] Null response.") +} +function yandexTTSUrl(text, tl, +speaker, emotion, i, group) { +speaker = NULLSTR +emotion = NULLSTR +split(Option["narrator"], group, ",") +for (i in group) { +if (group[i] ~ /^(g(ood)?|n(eutral)?|e(vil)?)$/) +emotion = group[i] +else if (group[i] ~ /^(f(emale)?|w(oman)?)$/) +speaker = "alyss" +else if (group[i] ~ /^m(ale|an)?$/) +speaker = "zahar" +else +speaker = group[i] +} +switch (tl) { +case "ar": tl = "ar_AE"; break +case "cs": tl = "cs_CZ"; break +case "da": tl = "da_DK"; break +case "de": tl = "de_DE"; break +case "el": tl = "el_GR"; break +case "en": tl = "en_GB"; break +case "es": tl = "es_ES"; break +case "fi": tl = "fi_FI"; break +case "fr": tl = "fr_FR"; break +case "it": tl = "it_IT"; break +case "nl": tl = "nl_NL"; break +case "no": tl = "no_NO"; break +case "pl": tl = "pl_PL"; break +case "pt": tl = "pt_PT"; break +case "ru": tl = "ru_RU"; break +case "sv": tl = "sv_SE"; break +case "tr": tl = "tr_TR"; break +default: tl = NULLSTR +} +return HttpProtocol "tts.voicetech.yandex.net" "/tts?"\ +"text=" preprocess(text) (tl ? "&lang=" tl : tl)\ +(speaker ? "&speaker=" speaker : speaker)\ +(emotion ? "&emotion=" emotion : emotion)\ +"&format=mp3" "&quality=hi" +} +function yandexWebTranslateUrl(uri, sl, tl, hl) { +gsub(/:\/\//, "/", uri) +return HttpProtocol YandexWebTranslate "/proxy_u/"\ +(sl == "auto" ? tl : sl "-" tl)"/" uri +} +function yandexTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +r, +content, tokens, ast, +_sl, _tl, _hl, il, isPhonetic, +translation, +wShowOriginal, wShowTranslation, wShowLanguages, +wShowDictionary, dicContent, dicTokens, dicAst, +i, syn, mean, +group, temp) { +isPhonetic = match(tl, /^@/) +tl = substr(tl, 1 + isPhonetic) +if (!getCode(tl)) { +w("[WARNING] Unknown target language code: " tl) +} else if (isRTL(tl)) { +if (!FriBidi) +w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.") +} +_sl = getCode(sl); if (!_sl) _sl = sl +_tl = getCode(tl); if (!_tl) _tl = tl +_hl = getCode(hl); if (!_hl) _hl = hl +content = getResponse(text, _sl, _tl, _hl) +if (Option["dump"]) +return content +tokenize(tokens, content) +parseJson(ast, tokens) +l(content, "content", 1, 1) +l(tokens, "tokens", 1, 0, 1) +l(ast, "ast") +if (!isarray(ast) || !anything(ast)) { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +ExitCode = 1 +return +} +if (ast[0 SUBSEP "code"] != "200") { +e("[ERROR] " unparameterize(ast[0 SUBSEP "message"])) +ExitCode = 1 +return +} +translation = unparameterize(ast[0 SUBSEP "text" SUBSEP 0]) +wShowTranslationPhonetics = Option["show-translation-phonetics"] +if (wShowTranslationPhonetics && _tl != "emj") { +split(_tl, group, "-") +data = yandexPostRequestBody(translation, group[1], group[1], _hl, "translit") +content = curlPost("https://translate.yandex.net/translit/translit", data) +phonetics = (content ~ /not supported$/) ? "" : unparameterize(content) +} +split(unparameterize(ast[0 SUBSEP "lang"]), group, "-") +returnIl[0] = il = group[1] +if (Option["verbose"] < -1) +return il +else if (Option["verbose"] < 0) +return getList(il) +if (!isVerbose) { +r = isPhonetic && phonetics ? +prettify("brief-translation-phonetics", join(phonetics, " ")) : +prettify("brief-translation", s(translation, tl)) +} else { +wShowOriginal = Option["show-original"] +wShowTranslation = Option["show-translation"] +wShowLanguages = Option["show-languages"] +wShowDictionary = Option["show-dictionary"] +wShowOriginalPhonetics = Option["show-original-phonetics"] +if (wShowTranslationPhonetics && il != "emj") { +split(il, group, "-") +data = yandexPostRequestBody(text, group[1], group[1], _hl, "translit") +content = curlPost("https://translate.yandex.net/translit/translit", data) +oPhonetics = (content ~ /not supported$/) ? "" : unparameterize(content) +} +if (!oPhonetics) wShowOriginalPhonetics = 0 +if (!phonetics) wShowTranslationPhonetics = 0 +if (wShowOriginal) { +if (r) r = r RS RS +r = r m("-- display original text & phonetics") +r = r prettify("original", s(text, il)) +if (wShowOriginalPhonetics) +r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), il)) +} +if (wShowTranslation) { +if (r) r = r RS RS +r = r m("-- display major translation") +r = r prettify("translation", s(translation, tl)) +if (wShowTranslationPhonetics) +r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl)) +} +if (wShowLanguages) { +if (r) r = r RS RS +r = r m("-- display source language -> target language") +temp = Option["fmt-languages"] +if (!temp) temp = "[ %s -> %t ]" +split(temp, group, /(%s|%S|%t|%T)/) +r = r prettify("languages", group[1]) +if (temp ~ /%s/) +r = r prettify("languages-sl", getDisplay(il)) +if (temp ~ /%S/) +r = r prettify("languages-sl", getName(il)) +r = r prettify("languages", group[2]) +if (temp ~ /%t/) +r = r prettify("languages-tl", getDisplay(tl)) +if (temp ~ /%T/) +r = r prettify("languages-tl", getName(tl)) +r = r prettify("languages", group[3]) +} +if (wShowDictionary && false) { +dicContent = yandexGetDictionaryResponse(text, il, _tl, _hl) +tokenize(dicTokens, dicContent) +parseJson(dicAst, dicTokens) +if (anything(dicAst)) { +if (r) r = r RS +r = r m("-- display dictionary entries") +saveSortedIn = PROCINFO["sorted_in"] +PROCINFO["sorted_in"] = "@ind_num_asc" +for (i in dicAst) { +if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\ +"pos$") { +r = r RS prettify("dictionary-word-class", s((literal(dicAst[i])), hl)) +syn = mean = "" +} +if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\ +"tr" SUBSEP "[[:digit:]]+" SUBSEP\ +"mean" SUBSEP "[[:digit:]]+" SUBSEP "text") { +if (mean) { +mean = mean prettify("dictionary-explanation", ", ")\ +prettify("dictionary-explanation-item", s((literal(dicAst[i])), sl)) +} else { +mean = prettify("dictionary-explanation-item", s((literal(dicAst[i])), sl)) +} +} +if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\ +"tr" SUBSEP "[[:digit:]]+" SUBSEP\ +"syn" SUBSEP "[[:digit:]]+" SUBSEP "text") { +if (syn) { +syn = syn prettify("dictionary-explanation", ", ")\ +prettify("dictionary-word", s((literal(dicAst[i])), il)) +} else { +syn = prettify("dictionary-word", s((literal(dicAst[i])), il)) +} +} +if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\ +"tr" SUBSEP "[[:digit:]]+" SUBSEP "text$") { +text = prettify("dictionary-word", s((literal(dicAst[i])), il)) +if (syn) { +r = r RS ins(1, text prettify("dictionary-explanation", ", ") syn) +} else { +r = r RS ins(1, text) +} +r = r RS ins(2, mean) +syn = mean = "" +} +} +PROCINFO["sorted_in"] = saveSortedIn +} +} +} +if (toSpeech) { +returnPlaylist[0]["text"] = translation +returnPlaylist[0]["tl"] = _tl +} +return r +} +BEGIN { provides("apertium") } +function apertiumInit() { +HttpProtocol = "http://" +HttpHost = "www.apertium.org" +HttpPort = 80 +} +function apertiumRequestUrl(text, sl, tl, hl) { +return HttpPathPrefix "/apy/translate?"\ +"langpair=" preprocess(sl) "|" preprocess(tl)\ +"&q=" preprocess(text) +} +function apertiumTTSUrl(text, tl, narrator) { +} +function apertiumWebTranslateUrl(uri, sl, tl, hl) { +} +function apertiumTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +r, +content, tokens, ast, +_sl, _tl, _hl, il, +translation, +wShowOriginal, wShowTranslation, wShowLanguages, +group, temp) { +if (!getCode(tl)) { +w("[WARNING] Unknown target language code: " tl) +} else if (isRTL(tl)) { +if (!FriBidi) +w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.") +} +_sl = getCode(sl); if (!_sl) _sl = sl +_tl = getCode(tl); if (!_tl) _tl = tl +_hl = getCode(hl); if (!_hl) _hl = hl +_sl = "auto" == _sl ? "en" : _sl +content = getResponse(text, _sl, _tl, _hl) +if (Option["dump"]) +return content +tokenize(tokens, content) +parseJson(ast, tokens) +l(content, "content", 1, 1) +l(tokens, "tokens", 1, 0, 1) +l(ast, "ast") +if (!isarray(ast) || !anything(ast)) { +e("[ERROR] Oops! Something went wrong and I can't translate it for you :(") +ExitCode = 1 +return +} +translation = uprintf(unquote(unparameterize(ast[0 SUBSEP "responseData" SUBSEP "translatedText"]))) +returnIl[0] = il = _sl +if (Option["verbose"] < -1) +return il +else if (Option["verbose"] < 0) +return getList(il) +if (!isVerbose) { +r = translation +} else { +wShowOriginal = Option["show-original"] +wShowTranslation = Option["show-translation"] +wShowLanguages = Option["show-languages"] +if (wShowOriginal) { +if (r) r = r RS RS +r = r m("-- display original text") +r = r prettify("original", s(text, il)) +} +if (wShowTranslation) { +if (r) r = r RS RS +r = r m("-- display major translation") +r = r prettify("translation", s(translation, tl)) +} +if (wShowLanguages) { +if (r) r = r RS RS +r = r m("-- display source language -> target language") +temp = Option["fmt-languages"] +if (!temp) temp = "[ %s -> %t ]" +split(temp, group, /(%s|%S|%t|%T)/) +r = r prettify("languages", group[1]) +if (temp ~ /%s/) +r = r prettify("languages-sl", getDisplay(il)) +if (temp ~ /%S/) +r = r prettify("languages-sl", getName(il)) +r = r prettify("languages", group[2]) +if (temp ~ /%t/) +r = r prettify("languages-tl", getDisplay(tl)) +if (temp ~ /%T/) +r = r prettify("languages-tl", getName(tl)) +r = r prettify("languages", group[3]) +} +} +if (toSpeech) { +returnPlaylist[0]["text"] = translation +returnPlaylist[0]["tl"] = _tl +} +return r +} +BEGIN { +provides("spell") +provides("aspell") +provides("hunspell") +} +function spellInit() { +Ispell = detectProgram("aspell", "--version") ? "aspell" : +(detectProgram("hunspell", "--version") ? "hunspell" : "") +if (!Ispell) { +e("[ERROR] Spell checker (aspell or hunspell) not found.") +exit 1 +} +} +function aspellInit() { +if (!(Ispell = detectProgram("aspell", "--version") ? "aspell" : "")) { +e("[ERROR] Spell checker (aspell) not found.") +exit 1 +} +} +function hunspellInit() { +if (!(Ispell = detectProgram("hunspell", "--version") ? "hunspell" : "")) { +e("[ERROR] Spell checker (hunspell) not found.") +exit 1 +} +} +function spellTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl, +args, i, j, r, line, group, word, sug) { +args = " -a" (sl != "auto" ? " -d " sl : "") +if (system("echo" PIPE Ispell args SUPOUT SUPERR)) { +e("[ERROR] No dictionary for language: " sl) +exit 1 +} +i = 1 +r = "" +while ((("echo " parameterize(text) PIPE Ispell args SUPERR) |& getline line) > 0) { +match(line, +/^& (.*) [[:digit:]]+ [[:digit:]]+: ([^,]+)(, ([^,]+))?(, ([^,]+))?/, +group) +if (RSTART) { +ExitCode = 1 +word = group[1] +sug = "[" group[2] +if (group[4]) sug = sug "|" group[4] +if (group[6]) sug = sug "|" group[6] +sug = sug "]" +j = i + index(substr(text, i), word) - 1 +r = r substr(text, i, j - i) +r = r ansi("bold", ansi("red", word)) ansi("yellow", sug) +i = j + length(word) +} +} +r = r substr(text, i) +return r +} +function aspellTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl) { +return spellTranslate(text, sl, tl, hl) +} +function hunspellTranslate(text, sl, tl, hl, +isVerbose, toSpeech, returnPlaylist, returnIl) { +return spellTranslate(text, sl, tl, hl) +} +function spellTTSUrl(text, tl, narrator) { +e("[ERROR] Spell checker does not support TTS.") +ExitCode = 1 +return +} +function aspellTTSUrl(text, tl, narrator) { +return spellTTSUrl(text, tl) +} +function hunspellTTSUrl(text, tl, narrator) { +return spellTTSUrl(text, tl) +} +function spellWebTranslateUrl(uri, sl, tl, hl) { +e("[ERROR] Spell checker does not support web translation.") +ExitCode = 1 +return +} +function aspellWebTranslateUrl(uri, sl, tl, hl) { +return spellWebTranslateUrl(uri, sl, tl, hl) +} +function hunspellWebTranslateUrl(uri, sl, tl, hl) { +return spellWebTranslateUrl(uri, sl, tl, hl) +} +function loadOptions(script, i, j, tokens, name, value) { +tokenize(tokens, script) +for (i in tokens) { +if (tokens[i] ~ /^:/) { +name = substr(tokens[i], 2) +value = tokens[i + 1] +if (value ~ /^[+-]?((0|[1-9][0-9]*)|[.][0-9]*|(0|[1-9][0-9]*)[.][0-9]*)([Ee][+-]?[0-9]+)?$/) { +delete Option[name] +Option[name] = value +} else if (value == "false" || value == "true") { +delete Option[name] +Option[name] = yn(value) +} else if (value ~ /^".*"$/) { +delete Option[name] +Option[name] = literal(value) +} else if (value == "[") { +delete Option[name] +for (j = 1; tokens[i + j + 1] && tokens[i + j + 1] != "]"; j++) { +if (tokens[i + j + 1] ~ /^".*"$/) +Option[name][j] = literal(tokens[i + j + 1]) +else { +e("[ERROR] Malformed configuration.") +return +} +} +} else { +e("[ERROR] Malformed configuration.") +return +} +} +} +} +function upgrade( i, newVersion, registry, tokens) { +RegistryIndex = "https://raw.githubusercontent.com/soimort/translate-shell/registry/index.trans" +registry = curl(RegistryIndex) +if (!registry) { +e("[ERROR] Failed to check for upgrade.") +ExitCode = 1 +return +} +tokenize(tokens, registry) +for (i in tokens) +if (tokens[i] == ":translate-shell") +newVersion = literal(tokens[i + 1]) +if (newerVersion(newVersion, Version)) { +w("Current version: \t" Version) +w("New version available: \t" newVersion) +w("Download from: \t" "https://www.soimort.org/translate-shell/trans") +} else { +w("Current version: \t" Version) +w("Already up-to-date.") +} +} +function welcome() { +if (Option["fmt-welcome-message"]) +print prettify("welcome-message", Option["fmt-welcome-message"]) > STDERR +if (Option["fmt-welcome-submessage"]) +print prettify("welcome-submessage", Option["fmt-welcome-submessage"]) > STDERR +} +function prompt( i, p, temp) { +p = Option["fmt-prompt"] +if (p ~ /%a/) gsub(/%a/, strftime("%a"), p) +if (p ~ /%A/) gsub(/%A/, strftime("%A"), p) +if (p ~ /%b/) gsub(/%b/, strftime("%b"), p) +if (p ~ /%B/) gsub(/%B/, strftime("%B"), p) +if (p ~ /%c/) gsub(/%c/, strftime("%c"), p) +if (p ~ /%C/) gsub(/%C/, strftime("%C"), p) +if (p ~ /%d/) gsub(/%d/, strftime("%d"), p) +if (p ~ /%D/) gsub(/%D/, strftime("%D"), p) +if (p ~ /%e/) gsub(/%e/, strftime("%e"), p) +if (p ~ /%F/) gsub(/%F/, strftime("%F"), p) +if (p ~ /%g/) gsub(/%g/, strftime("%g"), p) +if (p ~ /%G/) gsub(/%G/, strftime("%G"), p) +if (p ~ /%h/) gsub(/%h/, strftime("%h"), p) +if (p ~ /%H/) gsub(/%H/, strftime("%H"), p) +if (p ~ /%I/) gsub(/%I/, strftime("%I"), p) +if (p ~ /%j/) gsub(/%j/, strftime("%j"), p) +if (p ~ /%m/) gsub(/%m/, strftime("%m"), p) +if (p ~ /%M/) gsub(/%M/, strftime("%M"), p) +if (p ~ /%n/) gsub(/%n/, strftime("%n"), p) +if (p ~ /%p/) gsub(/%p/, strftime("%p"), p) +if (p ~ /%r/) gsub(/%r/, strftime("%r"), p) +if (p ~ /%R/) gsub(/%R/, strftime("%R"), p) +if (p ~ /%u/) gsub(/%u/, strftime("%u"), p) +if (p ~ /%U/) gsub(/%U/, strftime("%U"), p) +if (p ~ /%V/) gsub(/%V/, strftime("%V"), p) +if (p ~ /%w/) gsub(/%w/, strftime("%w"), p) +if (p ~ /%W/) gsub(/%W/, strftime("%W"), p) +if (p ~ /%x/) gsub(/%x/, strftime("%x"), p) +if (p ~ /%X/) gsub(/%X/, strftime("%X"), p) +if (p ~ /%y/) gsub(/%y/, strftime("%y"), p) +if (p ~ /%Y/) gsub(/%Y/, strftime("%Y"), p) +if (p ~ /%z/) gsub(/%z/, strftime("%z"), p) +if (p ~ /%Z/) gsub(/%Z/, strftime("%Z"), p) +if (p ~ /%_/) +gsub(/%_/, showTranslationsOf(Option["hl"]), p) +if (p ~ /%l/) +gsub(/%l/, getDisplay(Option["hl"]), p) +if (p ~ /%L/) +gsub(/%L/, getName(Option["hl"]), p) +if (p ~ /%S/) { +temp = getName(Option["sls"][1]) +for (i = 2; i <= length(Option["sls"]); i++) +temp = temp "+" getName(Option["sls"][i]) +gsub(/%S/, temp, p) +} +if (p ~ /%t/) { +temp = getDisplay(Option["tl"][1]) +for (i = 2; i <= length(Option["tl"]); i++) +temp = temp "+" getDisplay(Option["tl"][i]) +gsub(/%t/, temp, p) +} +if (p ~ /%T/) { +temp = getName(Option["tl"][1]) +for (i = 2; i <= length(Option["tl"]); i++) +temp = temp "+" getName(Option["tl"][i]) +gsub(/%T/, temp, p) +} +if (p ~ /%,/) { +temp = getDisplay(Option["tl"][1]) +for (i = 2; i <= length(Option["tl"]); i++) +temp = temp "," getDisplay(Option["tl"][i]) +gsub(/%,/, temp, p) +} +if (p ~ /% STDERR +} +function repl(line, command, group, name, i, value, words) { +split(line, words, " ") +command = words[1] +if (command ~ /^:(q|quit)$/) { +exit +} else if (command ~ /^:set$/) { +name = words[2] +value = words[3] +Option[name] = value +} else if (command ~ /^:show$/) { +name = words[2] +print prettify("welcome-submessage", toString(Option[name], 1, 0, 1)) +} else if (command ~ /^:engine$/) { +value = words[2] +Option["engine"] = value +initHttpService() +} else { +match(command, /^[{(\[]?((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)?(:|=)((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)[})\]]?$/, group) +if (RSTART) { +if (group[1]) { +split(group[1], Option["sls"], "+") +Option["sl"] = Option["sls"][1] +} +if (group[7]) split(group[7], Option["tl"], "+") +line = words[2] +for (i = 3; i <= length(words); i++) +line = line " " words[i] +} +if (line) { +translates(line) +if (Option["verbose"]) printf RS +} +} +prompt() +} +function init() { +initGawk() +initBiDiTerm() +initBiDi() +initLocale() +initLocaleAlias() +initUserLang() +RS = "\n" +ExitCode = 0 +Option["debug"] = 0 +Option["engine"] = "google" +Option["verbose"] = 1 +Option["show-original"] = 1 +Option["show-original-phonetics"] = 1 +Option["show-translation"] = 1 +Option["show-translation-phonetics"] = 1 +Option["show-prompt-message"] = 1 +Option["show-languages"] = 1 +Option["show-original-dictionary"] = 0 +Option["show-dictionary"] = 1 +Option["show-alternatives"] = 1 +Option["width"] = ENVIRON["COLUMNS"] ? ENVIRON["COLUMNS"] - 2 : 0 +Option["indent"] = 4 +Option["no-ansi"] = 0 +Option["no-autocorrect"] = 0 +Option["no-bidi"] = 0 +Option["force-bidi"] = 0 +Option["no-warn"] = 0 +Option["theme"] = "default" +Option["dump"] = 0 +Option["play"] = 0 +Option["narrator"] = "female" +Option["player"] = ENVIRON["PLAYER"] +Option["no-translate"] = 0 +Option["download-audio"] = 0 +Option["download-audio-as"] = NULLSTR +Option["view"] = 0 +Option["pager"] = ENVIRON["PAGER"] +Option["browser"] = ENVIRON["BROWSER"] +Option["proxy"] = ENVIRON["HTTP_PROXY"] ? ENVIRON["HTTP_PROXY"] : ENVIRON["http_proxy"] +Option["user-agent"] = ENVIRON["USER_AGENT"] ? ENVIRON["USER_AGENT"] : +"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) "\ +"AppleWebKit/537.36 (KHTML, like Gecko) "\ +"Chrome/81.0.4044.138 "\ +"Safari/537.36" +Option["ip-version"] = 0 +Option["no-rlwrap"] = 0 +Option["interactive"] = 0 +Option["emacs"] = 0 +Option["input"] = NULLSTR +Option["output"] = STDOUT +Option["hl"] = ENVIRON["HOME_LANG"] ? ENVIRON["HOME_LANG"] : UserLang +Option["sl"] = ENVIRON["SOURCE_LANG"] ? ENVIRON["SOURCE_LANG"] : "auto" +Option["sls"][1] = Option["sl"] +Option["tl"][1] = ENVIRON["TARGET_LANG"] ? ENVIRON["TARGET_LANG"] : UserLang +Option["join-sentence"] = 0 +} +function initScript( file, line, script, temp) { +file = ".trans" +if (!fileExists(file)) { +file = ENVIRON["HOME"] "/.translate-shell/init.trans" +if (!fileExists(file)) { +file = ENVIRON["XDG_CONFIG_HOME"] "/translate-shell/init.trans" +if (!fileExists(file)) { +file = ENVIRON["HOME"] "/.config/translate-shell/init.trans" +if (!fileExists(file)) { +file = "/etc/translate-shell" +if (!fileExists(file)) return +} +} +} +} +InitScript = file +script = NULLSTR +while (getline line < InitScript) +script = script "\n" line +loadOptions(script) +if (!isarray(Option["tl"])) { +temp = Option["tl"] +delete Option["tl"] +Option["tl"][1] = temp +} +} +function initMisc( command, group, temp) { +initHttpService() +if (!Option["width"] && detectProgram("tput", "-V")) { +command = "tput cols" SUPERR +command | getline temp +close(command) +Option["width"] = temp > 5 ? temp - 2 : 64 +} +if (Option["no-ansi"]) +delete AnsiCode +if (Option["no-bidi"] || BiDiTerm == "mlterm") +BiDi = BiDiNoPad = NULLSTR +else if (!Option["force-bidi"] && BiDiTerm == "konsole") { +BiDiNoPad = NULLSTR +BiDi = "sed \"s/'/\\\\\\'/\" | xargs -0 printf '%%%ss'" +} +initLocaleDisplay() +if (Option["no-warn"]) +STDERR = "/dev/null" +if (Option["play"]) { +if (!Option["player"]) { +initAudioPlayer() +Option["player"] = AudioPlayer ? AudioPlayer : Option["player"] +if (!Option["player"]) +initSpeechSynthesizer() +} +if (!Option["player"] && !SpeechSynthesizer) { +w("[WARNING] No available audio player or speech synthesizer.") +Option["play"] = 0 +} +} +if (Option["view"]) { +if (!Option["pager"]) { +initPager() +Option["pager"] = Pager +} +if (!Option["pager"]) { +w("[WARNING] No available terminal pager.") +Option["view"] = 0 +} +} +if (!Option["browser"]) { +Platform = detectProgram("uname", "-s", 1) +Option["browser"] = Platform == "Darwin" ? "open" : "xdg-open" +} +} +BEGIN { +init() +if (!(belongsTo("-no-init", ARGV) || belongsTo("--no-init", ARGV))) +initScript() +pos = 0 +noargc = 0 +while (ARGV[++pos]) { +match(ARGV[pos], /^--?(V|vers(i(on?)?)?)$/) +if (RSTART) { +InfoOnly = "version" +continue +} +match(ARGV[pos], /^--?(H|h(e(lp?)?)?)$/) +if (RSTART) { +InfoOnly = "help" +continue +} +match(ARGV[pos], /^--?(M|m(a(n(u(al?)?)?)?)?)$/) +if (RSTART) { +InfoOnly = "manual" +continue +} +match(ARGV[pos], /^--?(T|ref(e(r(e(n(ce?)?)?)?)?)?)$/) +if (RSTART) { +InfoOnly = "reference" +continue +} +match(ARGV[pos], /^--?r$/) +if (RSTART) { +w("[WARNING] Option '-r' has been deprecated since version 0.9.\n"\ +" Use option '-T' or '-reference' instead.") +exit 1 +} +match(ARGV[pos], /^--?(R|reference-e(n(g(l(i(sh?)?)?)?)?)?)$/) +if (RSTART) { +InfoOnly = "reference-english" +continue +} +match(ARGV[pos], /^--?(L|list)(=(.*)?)?$/, group) +if (RSTART) { +InfoOnly = "list" +if (group[2]) { +if (group[3]) split(group[3], Option["tl"], "+") +} else +split(ARGV[++pos], Option["tl"], "+") +continue +} +match(ARGV[pos], /^--?(S|list-e(n(g(i(n(es?)?)?)?)?)?)$/) +if (RSTART) { +InfoOnly = "list-engines" +continue +} +match(ARGV[pos], /^--?(U|upgrade)$/) +if (RSTART) { +InfoOnly = "upgrade" +continue +} +match(ARGV[pos], /^--?(N|nothing)$/) +if (RSTART) { +InfoOnly = "nothing" +continue +} +match(ARGV[pos], /^--?(e|engine)(=(.*)?)?$/, group) +if (RSTART) { +Option["engine"] = group[2] ? +(group[3] ? group[3] : Option["engine"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^\/(.*)$/, group) +if (RSTART) { +Option["engine"] = group[1] +continue +} +match(ARGV[pos], /^--?verbose$/) +if (RSTART) { +Option["verbose"] = 1 +continue +} +match(ARGV[pos], /^--?b(r(i(ef?)?)?)?$/) +if (RSTART) { +Option["verbose"] = 0 +continue +} +match(ARGV[pos], /^--?d(i(c(t(i(o(n(a(ry?)?)?)?)?)?)?)?)?$/) +if (RSTART) { +Option["show-original-dictionary"] = 1 +Option["show-dictionary"] = 0 +Option["show-alternatives"] = 0 +continue +} +match(ARGV[pos], /^--?id(e(n(t(i(fy?)?)?)?)?)?$/) +if (RSTART) { +Option["verbose"] = Option["verbose"] - 2 +continue +} +match(ARGV[pos], /^--?show-original(=(.*)?)?$/, group) +if (RSTART) { +Option["show-original"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-original-phonetics(=(.*)?)?$/, group) +if (RSTART) { +Option["show-original-phonetics"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-translation(=(.*)?)?$/, group) +if (RSTART) { +Option["show-translation"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-translation-phonetics(=(.*)?)?$/, group) +if (RSTART) { +Option["show-translation-phonetics"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-prompt-message(=(.*)?)?$/, group) +if (RSTART) { +Option["show-prompt-message"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-languages(=(.*)?)?$/, group) +if (RSTART) { +Option["show-languages"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-original-dictionary(=(.*)?)?$/, group) +if (RSTART) { +Option["show-original-dictionary"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-dictionary(=(.*)?)?$/, group) +if (RSTART) { +Option["show-dictionary"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?show-alternatives(=(.*)?)?$/, group) +if (RSTART) { +Option["show-alternatives"] = yn(group[1] ? group[2] : ARGV[++pos]) +continue +} +match(ARGV[pos], /^--?w(i(d(th?)?)?)?(=(.*)?)?$/, group) +if (RSTART) { +Option["width"] = group[4] ? +(group[5] ? group[5] : Option["width"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?indent(=(.*)?)?$/, group) +if (RSTART) { +Option["indent"] = group[1] ? +(group[2] ? group[2] : Option["indent"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?theme(=(.*)?)?$/, group) +if (RSTART) { +Option["theme"] = group[1] ? +(group[2] ? group[2] : Option["theme"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?no-theme$/) +if (RSTART) { +Option["theme"] = NULLSTR +continue +} +match(ARGV[pos], /^--?no-ansi$/) +if (RSTART) { +Option["no-ansi"] = 1 +continue +} +match(ARGV[pos], /^--?no-auto(correct)?$/) +if (RSTART) { +Option["no-autocorrect"] = 1 +continue +} +match(ARGV[pos], /^--?no-bidi/) +if (RSTART) { +Option["no-bidi"] = 1 +continue +} +match(ARGV[pos], /^--?bidi/) +if (RSTART) { +Option["force-bidi"] = 1 +continue +} +match(ARGV[pos], /^--?no-warn/) +if (RSTART) { +Option["no-warn"] = 1 +continue +} +match(ARGV[pos], /^--?dump/) +if (RSTART) { +Option["dump"] = 1 +continue +} +match(ARGV[pos], /^--?p(l(ay?)?)?$/) +if (RSTART) { +Option["play"] = 1 +continue +} +match(ARGV[pos], /^--?sp(e(ak?)?)?$/) +if (RSTART) { +Option["play"] = 2 +continue +} +match(ARGV[pos], /^--?(n|narrator)(=(.*)?)?$/, group) +if (RSTART) { +if (!Option["play"]) Option["play"] = 1 +Option["narrator"] = group[2] ? +(group[3] ? group[3] : Option["narrator"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?player(=(.*)?)?$/, group) +if (RSTART) { +if (!Option["play"]) Option["play"] = 1 +Option["player"] = group[1] ? +(group[2] ? group[2] : Option["player"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?no-play$/) +if (RSTART) { +Option["play"] = 0 +continue +} +match(ARGV[pos], /^--?no-tran(s(l(a(te?)?)?)?)?$/) +if (RSTART) { +Option["no-translate"] = 1 +continue +} +match(ARGV[pos], /^--?download-a(u(d(io?)?)?)?$/) +if (RSTART) { +Option["download-audio"] = 1 +continue +} +match(ARGV[pos], /^--?download-audio-as(=(.*)?)?$/, group) +if (RSTART) { +if (!Option["download-audio"]) Option["download-audio"] = 1 +Option["download-audio-as"] = group[1] ? +(group[2] ? group[2] : Option["download-audio-as"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?v(i(ew?)?)?$/) +if (RSTART) { +Option["view"] = 1 +continue +} +match(ARGV[pos], /^--?pager(=(.*)?)?$/, group) +if (RSTART) { +Option["view"] = 1 +Option["pager"] = group[1] ? +(group[2] ? group[2] : Option["pager"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?no-(view|pager)$/) +if (RSTART) { +Option["view"] = 0 +continue +} +match(ARGV[pos], /^--?browser(=(.*)?)?$/, group) +if (RSTART) { +Option["browser"] = group[1] ? +(group[2] ? group[2] : Option["browser"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?no-browser$/) +if (RSTART) { +Option["browser"] = NONE +continue +} +match(ARGV[pos], /^--?(x|proxy)(=(.*)?)?$/, group) +if (RSTART) { +Option["proxy"] = group[2] ? +(group[3] ? group[3] : Option["proxy"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?(u|user-agent)(=(.*)?)?$/, group) +if (RSTART) { +Option["user-agent"] = group[2] ? +(group[3] ? group[3] : Option["user-agent"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?(4|ipv4|inet4-only)$/) +if (RSTART) { +Option["ip-version"] = 4 +continue +} +match(ARGV[pos], /^--?(6|ipv6|inet6-only)$/) +if (RSTART) { +Option["ip-version"] = 6 +continue +} +match(ARGV[pos], /^--?(I|int(e(r(a(c(t(i(ve?)?)?)?)?)?)?)?|shell)$/) +if (RSTART) { +Option["interactive"] = 1 +continue +} +match(ARGV[pos], /^--?(E|emacs)$/) +if (RSTART) { +Option["emacs"] = 1 +continue +} +match(ARGV[pos], /^--?no-rlwrap$/) +if (RSTART) { +Option["no-rlwrap"] = 1 +continue +} +match(ARGV[pos], /^--?prompt(=(.*)?)?$/, group) +if (RSTART) { +w("[ERROR] Option '-prompt' has been deprecated since version 0.9.\n"\ +" Use configuration variable 'fmt-prompt' instead.") +exit 1 +} +match(ARGV[pos], /^--?prompt-color(=(.*)?)?$/, group) +if (RSTART) { +w("[ERROR] Option '-prompt-color' has been deprecated since version 0.9.\n"\ +" Use configuration variable 'sgr-prompt' instead.") +exit 1 +} +match(ARGV[pos], /^--?i(n(p(ut?)?)?)?(=(.*)?)?$/, group) +if (RSTART) { +Option["input"] = group[4] ? +(group[5] ? group[5] : Option["input"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?o(u(t(p(ut?)?)?)?)?(=(.*)?)?$/, group) +if (RSTART) { +Option["output"] = group[5] ? +(group[6] ? group[6] : Option["output"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?(l(a(ng?)?)?|hl)(=(.*)?)?$/, group) +if (RSTART) { +Option["hl"] = group[4] ? +(group[5] ? group[5] : Option["hl"]) : +ARGV[++pos] +continue +} +match(ARGV[pos], /^--?(s(o(u(r(ce?)?)?)?|l)?|f|from)(=(.*)?)?$/, group) +if (RSTART) { +if (group[6]) { +if (group[7]) split(group[7], Option["sls"], "+") +} else +split(ARGV[++pos], Option["sls"], "+") +Option["sl"] = Option["sls"][1] +continue +} +match(ARGV[pos], /^--?t(a(r(g(et?)?)?)?|l|o)?(=(.*)?)?$/, group) +if (RSTART) { +if (group[5]) { +if (group[6]) split(group[6], Option["tl"], "+") +} else +split(ARGV[++pos], Option["tl"], "+") +continue +} +match(ARGV[pos], /^[{(\[]?((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)?(:|=)((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)[})\]]?$/, group) +if (RSTART) { +if (group[1]) { +split(group[1], Option["sls"], "+") +Option["sl"] = Option["sls"][1] +} +if (group[7]) split(group[7], Option["tl"], "+") +continue +} +match(ARGV[pos], /^--?j(o(i(n(-(s(e(n(t(e(n(ce?)?)?)?)?)?)?)?)?)?)?)?$/) +if (RSTART) { +Option["join-sentence"] = 1 +continue +} +match(ARGV[pos], /^--?(D|debug)$/) +if (RSTART) { +Option["debug"] = 1 +continue +} +match(ARGV[pos], /^--?no-init/) +if (RSTART) continue +match(ARGV[pos], /^-(-?no-op)?$/) +if (RSTART) continue +match(ARGV[pos], /^--$/) +if (RSTART) { +++pos +break +} +noargv[noargc++] = ARGV[pos] +} +if (Option["interactive"] && !Option["no-rlwrap"]) +rlwrapMe() +else if (Option["emacs"] && !Option["interactive"] && !Option["no-rlwrap"]) +if (emacsMe()) +Option["interactive"] = 1 +initMisc() +switch (InfoOnly) { +case "version": +print getVersion() +exit ExitCode +case "help": +print getHelp() +exit ExitCode +case "manual": +showMan() +exit ExitCode +case "reference": +print getReference("endonym") +exit ExitCode +case "reference-english": +print getReference("name") +exit ExitCode +case "list": +print getList(Option["tl"]) +exit ExitCode +case "list-engines": +for (translator in Translator) +print (Option["engine"] == translator ? "* " : " ") translator +exit ExitCode +case "upgrade": +upgrade() +exit ExitCode +case "nothing": +exit ExitCode +} +setTheme() +if (Option["interactive"]) +welcome() +if (pos < ARGC) +for (i = pos; i < ARGC; i++) +noargv[noargc++] = ARGV[i] +if (noargc > 1 && Option["join-sentence"]) { +noargv[0] = join(noargv, " ") +noargc = 1 +} +if (noargc) { +for (i = 0; i < noargc; i++) { +if (Option["verbose"] && i > pos) +p(prettify("source-seperator", replicate(Option["chr-source-seperator"], Option["width"]))) +translates(noargv[i], 1) +} +} else { +if (!Option["input"]) Option["input"] = STDIN +} +if (Option["input"]) +translateMain() +exit ExitCode +} +EOF +read -r -d '' TRANS_MANPAGE << 'EOF' +.\" Automatically generated by Pandoc 2.5 +.\" +.TH "TRANS" "1" "2020\-05\-11" "0.9.6.12" "" +.hy +.SH NAME +.PP +trans \- Command\-line translator using Google Translate, Bing +Translator, Yandex.Translate, etc. +.SH SYNOPSIS +.PP +\f[B]trans\f[R] [\f[I]OPTIONS\f[R]] +[\f[I]SOURCE\f[R]]:[\f[I]TARGETS\f[R]] [\f[I]TEXT\f[R]]... +.SH DESCRIPTION +.PP +This tool translates text into any language from the command\-line, +using a translation engine such as Google Translate, Bing Translator and +Yandex.Translate. +.PP +Each argument which is not a valid option is treated as \f[I]TEXT\f[R] +to be translated. +.PP +If neither \f[I]TEXT\f[R] nor the input file is specified by +command\-line arguments, the program will read and translate from +standard input. +.SH OPTIONS +.SS Information options +.TP +.B \f[B]\-V\f[R], \f[B]\-version\f[R] +Print version and exit. +.TP +.B \f[B]\-H\f[R], \f[B]\-help\f[R] +Print help message and exit. +\f[B]\-M\f[R], \f[B]\-man\f[R] +Show man page and exit. +.TP +.B \f[B]\-T\f[R], \f[B]\-reference\f[R] +Print reference table of all supported languages and codes, and exit. +Names of languages are displayed in their endonyms (language name in the +language itself). +.TP +.B \f[B]\-R\f[R], \f[B]\-reference\-english\f[R] +Print reference table of all supported languages and codes, and exit. +Names of languages are displayed in English. +.TP +.B \f[B]\-L\f[R] \f[I]CODES\f[R], \f[B]\-list\f[R] \f[I]CODES\f[R] +Print details of languages and exit. +When specifying two or more language codes, concatenate them by plus +sign \[dq]+\[dq]. +.TP +.B \f[B]\-S\f[R], \f[B]\-list\-engines\f[R] +List available translation engines and exit. +.TP +.B \f[B]\-U\f[R], \f[B]\-upgrade\f[R] +Check for upgrade of this program. +.SS Translator options +.TP +.B \f[B]\-e\f[R] \f[I]ENGINE\f[R], \f[B]\-engine\f[R] \f[I]ENGINE\f[R] +Specify the translation engine to use. +(default: google) +.SS Display options +.TP +.B \f[B]\-verbose\f[R] +Verbose mode. +.RS +.PP +Show the original text and its most relevant translation, then its +phonetic notation (if any), then its alternative translations (if any) +or its definition in the dictionary (if it is a word). +.PP +This option is unnecessary in most cases since verbose mode is enabled +by default. +.RE +.TP +.B \f[B]\-b\f[R], \f[B]\-brief\f[R] +Brief mode. +.RS +.PP +Show the most relevant translation or its phonetic notation only. +.RE +.TP +.B \f[B]\-d\f[R], \f[B]\-dictionary\f[R] +Dictionary mode. +.RS +.PP +Show the definition of the original word in the dictionary. +.RE +.TP +.B \f[B]\-identify\f[R] +Language identification. +.RS +.PP +Show the identified language of the original text. +.RE +.TP +.B \f[B]\-show\-original\f[R] \f[I]Y/n\f[R] +Show original text or not. +(default: yes) +.TP +.B \f[B]\-show\-original\-phonetics\f[R] \f[I]Y/n\f[R] +Show phonetic notation of original text or not. +(default: yes) +.TP +.B \f[B]\-show\-translation\f[R] \f[I]Y/n\f[R] +Show translation or not. +(default: yes) +.TP +.B \f[B]\-show\-translation\-phonetics\f[R] \f[I]Y/n\f[R] +Show phonetic notation of translation or not. +(default: yes) +.TP +.B \f[B]\-show\-prompt\-message\f[R] \f[I]Y/n\f[R] +Show prompt message or not. +(default: yes) +.TP +.B \f[B]\-show\-languages\f[R] \f[I]Y/n\f[R] +Show source and target languages or not. +(default: yes) +.TP +.B \f[B]\-show\-original\-dictionary\f[R] \f[I]y/N\f[R] +Show dictionary entry of original text or not. +(default: no) +.RS +.PP +This option is enabled in dictionary mode. +.RE +.TP +.B \f[B]\-show\-dictionary\f[R] \f[I]Y/n\f[R] +Show dictionary entry of translation or not. +(default: yes) +.TP +.B \f[B]\-show\-alternatives\f[R] \f[I]Y/n\f[R] +Show alternative translations or not. +(default: yes) +.TP +.B \f[B]\-w\f[R] \f[I]NUM\f[R], \f[B]\-width\f[R] \f[I]NUM\f[R] +Specify the screen width for padding. +.RS +.PP +This option overrides the setting of environment variable +$\f[B]COLUMNS\f[R]. +.RE +.TP +.B \f[B]\-indent\f[R] \f[I]NUM\f[R] +Specify the size of indent (number of spaces). +(default: 4) +.TP +.B \f[B]\-theme\f[R] \f[I]FILENAME\f[R] +Specify the theme to use. +(default: default) +.TP +.B \f[B]\-no\-theme\f[R] +Do not use any other theme than default. +.TP +.B \f[B]\-no\-ansi\f[R] +Do not use ANSI escape codes. +.TP +.B \f[B]\-no\-autocorrect\f[R] +Do not autocorrect. +(if defaulted by the translation engine) +.TP +.B \f[B]\-no\-bidi\f[R] +Do not convert bidirectional texts. +.TP +.B \f[B]\-bidi\f[R] +Always convert bidirectional texts. +.TP +.B \f[B]\-no\-warn\f[R] +Do not write warning messages to stderr. +.TP +.B \f[B]\-dump\f[R] +Print raw API response instead. +.SS Audio options +.TP +.B \f[B]\-p\f[R], \f[B]\-play\f[R] +Listen to the translation. +.RS +.PP +You must have at least one of the supported audio players +(\f[B]mplayer\f[R], \f[B]mpv\f[R] or \f[B]mpg123\f[R]) installed to +stream from Google Text\-to\-Speech engine. +Otherwise, a local speech synthesizer may be used instead (\f[B]say\f[R] +on macOS, \f[B]espeak\f[R] on Linux or other platforms). +.RE +.TP +.B \f[B]\-speak\f[R] +Listen to the original text. +.TP +.B \f[B]\-n\f[R] \f[I]VOICE\f[R], \f[B]\-narrator\f[R] \f[I]VOICE\f[R] +Specify the narrator, and listen to the translation. +.RS +.PP +Common values for this option are \f[B]male\f[R] and \f[B]female\f[R]. +.RE +.TP +.B \f[B]\-player\f[R] \f[I]PROGRAM\f[R] +Specify the audio player to use, and listen to the translation. +.RS +.PP +Option \f[B]\-play\f[R] will try to use \f[B]mplayer\f[R], \f[B]mpv\f[R] +or \f[B]mpg123\f[R] by default, since these players are known to work +for streaming URLs. +Not all command\-line audio players can work this way. +Use this option only when you have your own preference. +.PP +This option overrides the setting of environment variable +$\f[B]PLAYER\f[R]. +.RE +.TP +.B \f[B]\-no\-play\f[R] +Do not listen to the translation. +.TP +.B \f[B]\-no\-translate\f[R] +Do not translate anything when using \-speak. +.TP +.B \f[B]\-download\-audio\f[R] +Download the audio to the current directory. +.TP +.B \f[B]\-download\-audio\-as\f[R] \f[I]FILENAME\f[R] +Download the audio to the specified file. +.SS Terminal paging and browsing options +.TP +.B \f[B]\-v\f[R], \f[B]\-view\f[R] +View the translation in a terminal pager (\f[B]less\f[R], \f[B]more\f[R] +or \f[B]most\f[R]). +.TP +.B \f[B]\-pager\f[R] \f[I]PROGRAM\f[R] +Specify the terminal pager to use, and view the translation. +.RS +.PP +This option overrides the setting of environment variable +$\f[B]PAGER\f[R]. +.RE +.TP +.B \f[B]\-no\-view\f[R], \f[B]\-no\-pager\f[R] +Do not view the translation in a terminal pager. +.TP +.B \f[B]\-browser\f[R] \f[I]PROGRAM\f[R] +Specify the web browser to use. +.RS +.PP +This option overrides the setting of environment variable +$\f[B]BROWSER\f[R]. +.RE +.TP +.B \f[B]\-no\-browser\f[R] +Do not open the web browser. +.SS Networking options +.TP +.B \f[B]\-x\f[R] \f[I]HOST:PORT\f[R], \f[B]\-proxy\f[R] \f[I]HOST:PORT\f[R] +Use HTTP proxy on given port. +.RS +.PP +This option overrides the setting of environment variables +$\f[B]HTTP_PROXY\f[R] and $\f[B]http_proxy\f[R]. +.RE +.TP +.B \f[B]\-u\f[R] \f[I]STRING\f[R], \f[B]\-user\-agent\f[R] \f[I]STRING\f[R] +Specify the User\-Agent to identify as. +.RS +.PP +This option overrides the setting of environment variables +$\f[B]USER_AGENT\f[R]. +.RE +.TP +.B \f[B]\-4\f[R], \f[B]\-ipv4\f[R], \f[B]\-inet4\-only\f[R] +Connect only to IPv4 addresses. +.TP +.B \f[B]\-6\f[R], \f[B]\-ipv6\f[R], \f[B]\-inet6\-only\f[R] +Connect only to IPv6 addresses. +.SS Interactive shell options +.TP +.B \f[B]\-I\f[R], \f[B]\-interactive\f[R], \f[B]\-shell\f[R] +Start an interactive shell, invoking \f[B]rlwrap\f[R] whenever possible +(unless \f[B]\-no\-rlwrap\f[R] is specified). +.TP +.B \f[B]\-E\f[R], \f[B]\-emacs\f[R] +Start the GNU Emacs front\-end for an interactive shell. +.RS +.PP +This option does not need to, and cannot be used along with +\f[B]\-I\f[R] or \f[B]\-no\-rlwrap\f[R]. +.RE +.TP +.B \f[B]\-no\-rlwrap\f[R] +Do not invoke \f[B]rlwrap\f[R] when starting an interactive shell. +.RS +.PP +This option is useful when your terminal type is not supported by +\f[B]rlwrap\f[R] (e.g. +\f[B]emacs\f[R]). +.RE +.SS I/O options +.TP +.B \f[B]\-i\f[R] \f[I]FILENAME\f[R], \f[B]\-input\f[R] \f[I]FILENAME\f[R] +Specify the input file. +.RS +.PP +Source text to be translated will be read from the input file, instead +of standard input. +.RE +.TP +.B \f[B]\-o\f[R] \f[I]FILENAME\f[R], \f[B]\-output\f[R] \f[I]FILENAME\f[R] +Specify the output file. +.RS +.PP +Translations will be written to the output file, instead of standard +output. +.RE +.SS Language preference options +.TP +.B \f[B]\-l\f[R] \f[I]CODE\f[R], \f[B]\-hl\f[R] \f[I]CODE\f[R], \f[B]\-lang\f[R] \f[I]CODE\f[R] +Specify your home language (the language you would like to see for +displaying prompt messages in the translation). +.RS +.PP +This option affects only the display in verbose mode (anything other +than source language and target language will be displayed in your home +language). +This option has no effect in brief mode. +.PP +This option is optional. +When its setting is omitted, English will be used. +.PP +This option overrides the setting of environment variables +$\f[B]LC_ALL\f[R], $\f[B]LANG\f[R], and $\f[B]HOME_LANG\f[R]. +.RE +.TP +.B \f[B]\-s\f[R] \f[I]CODES\f[R], \f[B]\-sl\f[R] \f[I]CODES\f[R], \f[B]\-source\f[R] \f[I]CODES\f[R], \f[B]\-from\f[R] \f[I]CODES\f[R] +Specify the source language(s) (the language(s) of original text). +When specifying two or more language codes, concatenate them by plus +sign \[dq]+\[dq]. +.RS +.PP +This option is optional. +When its setting is omitted, the language of original text will be +identified automatically (with a possibility of misidentification). +.PP +This option overrides the setting of environment variable +$\f[B]SOURCE_LANG\f[R]. +.RE +.TP +.B \f[B]\-t\f[R] \f[I]CODES\f[R], \f[B]\-tl\f[R] \f[I]CODES\f[R], \f[B]\-target\f[R] \f[I]CODES\f[R], \f[B]\-to\f[R] \f[I]CODES\f[R] +Specify the target language(s) (the language(s) of translated text). +When specifying two or more language codes, concatenate them by plus +sign \[dq]+\[dq]. +.RS +.PP +This option is optional. +When its setting is omitted, everything will be translated into English. +.PP +This option overrides the setting of environment variables +$\f[B]LC_ALL\f[R], $\f[B]LANG\f[R], and $\f[B]TARGET_LANG\f[R]. +.RE +.TP +.B [\f[I]SOURCE\f[R]]:[\f[I]TARGETS\f[R]] +A simpler, alternative way to specify the source language and target +language(s) is to use a shortcut formatted string: +.RS +.IP \[bu] 2 +\f[I]SOURCE\-CODE\f[R]:\f[I]TARGET\-CODE\f[R] +.IP \[bu] 2 +\f[I]SOURCE\-CODE\f[R]:\f[I]TARGET\-CODE1\f[R]+\f[I]TARGET\-CODE2\f[R]+... +.IP \[bu] 2 +\f[I]SOURCE\-CODE\f[R]=\f[I]TARGET\-CODE\f[R] +.IP \[bu] 2 +\f[I]SOURCE\-CODE\f[R]=\f[I]TARGET\-CODE1\f[R]+\f[I]TARGET\-CODE2\f[R]+... +.PP +Delimiter \[dq]:\[dq] and \[dq]=\[dq] can be used interchangeably. +.PP +Either \f[I]SOURCE\f[R] or \f[I]TARGETS\f[R] may be omitted, but the +delimiter character must be kept. +.RE +.SS Text preprocessing options +.TP +.B \f[B]\-j\f[R], \f[B]\-join\-sentence\f[R] +Treat all arguments as one single sentence. +.SS Other options +.TP +.B \f[B]\-no\-init\f[R] +Do not load any initialization script. +.TP +.B \f[B]\-\-\f[R] +End\-of\-options. +.RS +.PP +All arguments after this option are treated as \f[I]TEXT\f[R] to be +translated. +.RE +.SH EXIT STATUS +.TP +.B \f[B]0\f[R] +Successful translation. +.TP +.B \f[B]1\f[R] +Error. +.SH ENVIRONMENT +.TP +.B \f[B]PAGER\f[R] +Equivalent to option setting \f[B]\-pager\f[R]. +.TP +.B \f[B]BROWSER\f[R] +Equivalent to option setting \f[B]\-browser\f[R]. +.TP +.B \f[B]PLAYER\f[R] +Equivalent to option setting \f[B]\-player\f[R]. +.TP +.B \f[B]HTTP_PROXY\f[R] +Equivalent to option setting \f[B]\-proxy\f[R]. +.TP +.B \f[B]USER_AGENT\f[R] +Equivalent to option setting \f[B]\-user\-agent\f[R]. +.TP +.B \f[B]HOME_LANG\f[R] +Equivalent to option setting \f[B]\-lang\f[R]. +.TP +.B \f[B]SOURCE_LANG\f[R] +Equivalent to option setting \f[B]\-source\f[R]. +.TP +.B \f[B]TARGET_LANG\f[R] +Equivalent to option setting \f[B]\-target\f[R]. +.SH FILES +.TP +.B \f[I]/etc/translate\-shell\f[R] +Initialization script. +(system\-wide) +.TP +.B \f[I]$HOME/.translate\-shell/init.trans\f[R] +Initialization script. +(user\-specific) +.TP +.B \f[I]$XDG_CONFIG_HOME/translate\-shell/init.trans\f[R] +Initialization script. +(user\-specific) +.TP +.B \f[I]./.trans\f[R] +Initialization script. +(current directory) +.SH FURTHER DOCUMENTATION +.PP + +.SH REPORTING BUGS +.PP + +.SH AUTHORS +Mort Yao . +EOF +export TRANS_MANPAGE +export TRANS_BUILD=release +gawk -f <(echo -E "$TRANS_PROGRAM") - "$@" diff --git a/.local/bin/twitch b/.local/bin/twitch new file mode 100755 index 0000000..688f3b8 --- /dev/null +++ b/.local/bin/twitch @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +CLIENT_ID=SET_CLIENT_ID_HERE +OAUTH_TOKEN=SET_OAUTH_TOKEN_HERE + +if [ -z "$1" ]; then + curl -s \ + -H 'Accept: application/vnd.twitchtv.v5+json' \ + -H "Client-ID: ${TWITCH_CLIENT_ID:-$CLIENT_ID}" \ + -H "Authorization: OAuth ${TWITCH_OAUTH_TOKEN:-$OAUTH_TOKEN}" \ + -X GET 'https://api.twitch.tv/kraken/streams/followed' | \ + ramda \ + 'tap (res) -> if res.error then console.error(res); process.exit(1)' \ + '.streams' \ + 'map pick-dot-paths [\channel.name, \channel.status, \game, \viewers]' \ + 'map flat' \ + 'map (rename-keys-by split(".") >> last)' \ + '-> if is-empty(it) then "No one you are following is streaming right now." else it' \ + -o table +else + streamlink -p mpv twitch.tv/$1 ${2:-best} +fi diff --git a/.local/bin/update b/.local/bin/update new file mode 100755 index 0000000..57e9873 --- /dev/null +++ b/.local/bin/update @@ -0,0 +1,18 @@ +#!/bin/bash +echo +echo +echo -e $(date) +echo +echo +sudo /home/yorune/.local/bin/eix-repos-sync +sudo eix-sync -a +sudo eix-update +#sudo emerge-webrsync +#sudo emerge --sync +#sudo emaint sync -a +sudo emerge -auDN @world +echo +echo +echo -e $(date) +echo +echo diff --git a/.local/bin/update-kernel b/.local/bin/update-kernel new file mode 100755 index 0000000..2c2fc2b --- /dev/null +++ b/.local/bin/update-kernel @@ -0,0 +1,118 @@ +#!/bin/bash + +BACKUP="/home/yorune/Linux/portage" +LOG_FILE="/tmp/update-kernel.log" +TMP_KERNEL="/tmp/kernel-config-`uname -r`" +DEFAULT_KERNEL="/usr/src/linux/.config" + +function starting() { +echo -e "\e[93m----------------------COMPILING------------------------------\e[0m" +sudo cp -rv $DEFAULT_KERNEL $TMP_KERNEL +cp -r $TMP_KERNEL $BACKUP/kernel-config +cp -r /etc/portage/* $BACKUP +qlist -I | sort -u > $BACKUP/list-of-programs +} + +function selection() { +echo -e "\e[93m----------------------SELECTION-----------------------------\e[0m" +sudo eselect kernel list +echo +echo +read -p "New kernel is: " KERVER +echo +echo -e "Your kernel now is \e[91m$(uname -sr)\e[0m" +echo -e "Your selected kernel is \e[91m"$KERVER"\e[0m" +sudo eselect kernel set $KERVER +sudo eselect kernel list | grep "*" +echo +} + +function compilation() { +read -p "Do you want to accept and compile (Y/N): " agreed +echo +if [ "$agreed" == "y" ] || [ "$agreed" == "Y" ] + then + echo -e "\e[91m----------------------\e[5mSTARTING\e[0m\e[91m------------------------------\e[0m" && sleep 10 + $NOW > /tmp/compiling-starting + NEW_KERNEL="/tmp/new-kernel-config" + sudo cp -r $TMP_KERNEL $DEFAULT_KERNEL + cd /usr/src/linux; sudo make menuconfig; sleep 2; sudo cp -r $DEFAULT_KERNEL $NEW_KERNEL + sudo genkernel all --makeopts=-j$(nproc --all) --kernel-config=$NEW_KERNEL --callback="emerge nvidia-drivers::gentoo" --lvm --btrfs --luks + elif [ "$agreed" == "N" ] || [ "$agreed" == "n" ] + then + exit +fi +} + +function ending() { +echo +echo +echo -e "\e[93m----------------------CHECKING-----------------------------\e[0m" +checking +echo +echo +echo -e "\e[93m----------------------REMEMBER-----------------------------\e[0m" +echo "You can remove:" +echo "* /lib/modules/OLD_KERNEL" +echo "* /boot/initramfs-genkernel-OLD_KERNEL" +echo "* /boot/vmlinuz-OLD_KERNEL" +echo "* /boot/System.map-OLD_KERNEL" +echo "* /boot/initramfs-OLD_KERNEL" +echo "* /usr/src/linux-OLD_KERNEL" +echo +echo -e "AFTER EVERYTHING YOU MUST WRITE COMMAND \e[91m"sudo grub-mkconfig -o /boot/grub/grub.cfg"\e[0m" +} + +function checking() { +KERNEL=`eselect kernel list | awk '{print $2}' | egrep -o '[0-9]+.[0-9]+.[0-9]+' | tail -n1` + +INITRANFS="initramfs-$KERNEL-gentoo-x86_64.img" +SYSTEMMAP="System.map-$KERNEL-gentoo-x86_64" +VMLINUZ="vmlinuz-$KERNEL-gentoo-x86_64" + +ifchecking $INITRANFS +ifchecking $SYSTEMMAP +ifchecking $VMLINUZ +sudo grub-mkconfig -o /boot/grub/grub.cfg +} + +function ifchecking () { +FILE=/boot/$1 +if test -f "$FILE"; then + echo "$FILE exist in the /boot folder ;)" +else + echo "$FILE NOT exist in the /boot folder ;)" +fi +} + +function addgit() { + cd ~/Linux/portage || exit + git add . + git commit -m "Updated: $(date)" + git push + cd ~ || exit +} + +function main() { +clear + +BEGIN=$(date +"%s") + +starting +addgit +selection +compilation + +echo -e "\e[31mI am leaving! Thank You!\e[0m" && sleep 3 + +TERMIN=$(date +"%s") +DIFFTLPS=$(($TERMIN-$BEGIN)) + +echo -e "\e[93m------------------TIME COMPILATION-------------------------\e[0m" +echo -e "\e[93m$(($DIFFTLPS / 60)) minutes and $(($DIFFTLPS % 60)) seconds \e[0m elapsed for Script Execution." && sleep 3 +ending + +exit +} + +main diff --git a/.local/bin/video-convert b/.local/bin/video-convert new file mode 100755 index 0000000..6f44717 --- /dev/null +++ b/.local/bin/video-convert @@ -0,0 +1,3 @@ +#!/bin/bash +FILE=$(echo $1 | cut -d. -f1) +ffmpeg -i $1 -b:a 128k -codec:v libx265 -b:v 1500k -vf scale=1280:720 $FILE-convered.mp4 diff --git a/.local/bin/volume b/.local/bin/volume new file mode 100755 index 0000000..29ab7f1 --- /dev/null +++ b/.local/bin/volume @@ -0,0 +1,5 @@ +#!/bin/bash +pkill -RTMIN+10 dwmblocks +[ "$1" = "up" ] && ponymix increase 5 > /dev/null && notify-send "🔊 Volume $(ponymix get-volume)" +[ "$1" = "down" ] && ponymix decrease 5 > /dev/null && notify-send "🔉 Volume $(ponymix get-volume)" +[ "$1" = "toggle" ] && ponymix toggle> /dev/null && notify-send "🔇 Volume (un)mute " diff --git a/.local/bin/welcomer b/.local/bin/welcomer new file mode 100755 index 0000000..97125d6 --- /dev/null +++ b/.local/bin/welcomer @@ -0,0 +1,54 @@ +#!/bin/bash + +MEMTOTAL=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemTotal" | awk '{print $2}'` +MEMFREE=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemAvailable" | awk '{print $2}'` +NETE=`ip a | awk {'print $2'} | grep -i en | sed 's/://g'` +NETW=`ip a | awk {'print $2'} | grep -i wl | sed 's/://g'` +BACKUP_SDA8=`df -h | grep -i sda8 | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'` +BACKUP_EL=`df -h | grep /run/media/jaqu3/ELEMENTS | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'` +BACKUP_EV=`df -h | grep /run/media/jaqu3/BACKUP_EVERYTHING | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'` + + +if [ -z "$NETW" ]; then + NETD=$NETE +else + NETD=$NETW +fi + +if [ -z "$BACKUP_SDA8" ]; then + BACKUPSDA8=`echo NOT MOUNTED` +else + BACKUPSDA8=$BACKUP_SDA8 +fi + +if [ -z "$BACKUP_EL" ]; then + BACKUPEL=`echo NOT MOUNTED` +else + BACKUPEL=$BACKUP_EL +fi + +if [ -z "$BACKUP_EV" ]; then + BACKUPEV=`echo NOT MOUNTED` +else + BACKUPEV=$BACKUP_EV +fi + + echo -e " + \033[0;35m+++++++++++++++++++: \033[0;37mInfo\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + \033[0;37mHostname \033[0;35m= \033[1;32m `hostname` + \033[0;35m+ \033[0;37mAddress \033[0;35m= \033[1;32m `/sbin/ip addr show $NETD | grep "inet " | awk '{print $2}' | cut -d'/' -f1` + \033[0;35m+ \033[0;37mKernel \033[0;35m= \033[1;32m `uname -r` + \033[0;35m+ \033[0;37mUptime \033[0;35m= \033[1;32m`uptime | sed 's/.*up ([^,]*), .*/1/'` + \033[0;35m+ \033[0;37mMemory \033[0;35m= \033[1;32m $MEMTOTAL GB (free $MEMFREE GB)\033[0;35m + \033[0;35m+ \033[0;37mUsername \033[0;35m= \033[1;32m `whoami`\033[0;35m + + + \033[0;35m++++++++++++++++++: \033[0;37mStorage\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + \033[0;37m/ \033[0;35m= \033[1;32m `df -h / | sed '1,1d' | awk '{print $5\", \"$2\" (Total), \"$3\" (Usage)\"}'`\033[0;35m + + \033[0;37mSDA8 \033[0;35m= \033[1;32m $BACKUPSDA8 \033[0;35m + + \033[0;37mELEMENTS \033[0;35m= \033[1;32m $BACKUPEL \033[0;35m + + \033[0;37mBACKUP_EVERYTHING \033[0;35m= \033[1;32m $BACKUPEV \033[0;35m + + + \033[0;35m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \033[0m" + echo "" diff --git a/.local/bin/welcomer-serwer b/.local/bin/welcomer-serwer new file mode 100755 index 0000000..ef61673 --- /dev/null +++ b/.local/bin/welcomer-serwer @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copy this file to /bin/welcomer and change permisson +x +# sudo bash -c $'echo "/bin/welcomer" >> /etc/profile.d/mymotd.sh && chmod +x /etc/profile.d/mymotd.sh' + + +MEMTOTAL=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemTotal" | awk '{print $2}'` +MEMFREE=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemAvailable" | awk '{print $2}'` +NETE=`ip a | awk {'print $2'} | grep -i en | sed 's/://g'` +NETW=`ip a | awk {'print $2'} | grep -i wl | sed 's/://g'` + +if [ -z "$NETW" ]; then + NETD=$NETE +else + NETD=$NETW +fi + + echo -e " + \033[0;35m+++++++++++++++++++: \033[0;37mInfo\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + \033[0;37mHostname \033[0;35m= \033[1;32m `hostname` + \033[0;35m+ \033[0;37mAddress \033[0;35m= \033[1;32m `/sbin/ip addr show $NETD | grep "inet " | awk '{print $2}' | cut -d'/' -f1` + \033[0;35m+ \033[0;37mKernel \033[0;35m= \033[1;32m `uname -r` + \033[0;35m+ \033[0;37mUptime \033[0;35m= \033[1;32m`uptime | sed 's/.*up ([^,]*), .*/1/'` + \033[0;35m+ \033[0;37mMemory \033[0;35m= \033[1;32m $MEMTOTAL GB (free $MEMFREE GB)\033[0;35m + \033[0;35m+ \033[0;37mUsername \033[0;35m= \033[1;32m `whoami`\033[0;35m + + + \033[0;35m++++++++++++++++++: \033[0;37mStorage\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++ + + + + \033[0;37m/ \033[0;35m= \033[1;32m `df -h / | sed '1,1d' | awk '{print $5\", \"$2\" (Total), \"$3\" (Usage)\"}'`\033[0;35m + + + \033[0;35m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \033[0m" + echo "" diff --git a/.local/bin/wheel b/.local/bin/wheel new file mode 100755 index 0000000..67a4232 --- /dev/null +++ b/.local/bin/wheel @@ -0,0 +1,8 @@ +#!/usr/bin/python3.8 +# -*- coding: utf-8 -*- +import re +import sys +from wheel.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.local/bin/wsl-notify b/.local/bin/wsl-notify new file mode 100755 index 0000000..7e9f7bb --- /dev/null +++ b/.local/bin/wsl-notify @@ -0,0 +1,4 @@ +#!/bin/bash + + +/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe "New-BurntToastNotification -Text \"$1\"" diff --git a/.local/bin/wylaczoff b/.local/bin/wylaczoff new file mode 100755 index 0000000..bfb72eb --- /dev/null +++ b/.local/bin/wylaczoff @@ -0,0 +1,5 @@ +xset b off +xset s off && xset -dpms +set bell-style none +#/usr/bin/amixer -c 0 sset "Auto-Mute Mode" Disabled + diff --git a/.local/bin/yatqa b/.local/bin/yatqa new file mode 100755 index 0000000..b1df29b --- /dev/null +++ b/.local/bin/yatqa @@ -0,0 +1 @@ +wine "C:/Program Files (x86)/YaTQA/yatqa.exe" diff --git a/.local/bin/yt-mp3 b/.local/bin/yt-mp3 new file mode 100755 index 0000000..6827cdd --- /dev/null +++ b/.local/bin/yt-mp3 @@ -0,0 +1,2 @@ +#!/bin/bash +youtube-dl --extract-audio --audio-format mp3 $1 diff --git a/.local/bin/yt-video b/.local/bin/yt-video new file mode 100755 index 0000000..a3053b0 --- /dev/null +++ b/.local/bin/yt-video @@ -0,0 +1,2 @@ +#!/bin/bash +youtube-dl $1