diff options
| author | NPScript <nathan@reinerweb.ch> | 2022-04-09 21:22:20 +0200 |
|---|---|---|
| committer | NPScript <nathan@reinerweb.ch> | 2022-04-09 21:22:20 +0200 |
| commit | 896c311feb10e947c727a888308dbc7eb71d1ec2 (patch) | |
| tree | b6713478d14eb8ea531107c3f399c437db7fd2fb | |
init commit
62 files changed, 5324 insertions, 0 deletions
diff --git a/.config/dunst/dunstrc b/.config/dunst/dunstrc new file mode 100644 index 0000000..5f0b827 --- /dev/null +++ b/.config/dunst/dunstrc @@ -0,0 +1,413 @@ +[global] + ### Display ### + + # 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 + + # 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 = "300x5-15+42" + + # Show how many messages are currently hidden (because of geometry). + indicate_hidden = yes + + # 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 + + # The height of the entire notification. If the height is smaller + # than the font height and padding combined, it will be raised + # to the font height and padding. + notification_height = 0 + + # Draw a line of "separator_height" pixel height between two + # notifications. + # Set to 0 to disable. + separator_height = 1 + + # Padding between text and separator. + padding = 8 + + # Horizontal padding. + horizontal_padding = 8 + + # Defines width in pixels of frame around the notification window. + # Set to 0 to disable. + frame_width = 1 + + # Defines color of the frame around the notification window. + frame_color = "#547f62"" + + # 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 + + # Sort messages by urgency. + sort = yes + + # 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. + # A client can set the 'transient' hint to bypass this. See the rules + # section for how to disable this if necessary + idle_threshold = 120 + + ### Text ### + + font = SauceCodePro Nerd Font 10 + + # The spacing between lines. If the height is smaller than the + # font height, it will get raised to the font height. + line_height = 0 + + # Possible values are: + # full: Allow a small subset of html markup in notifications: + # <b>bold</b> + # <i>italic</i> + # <s>strikethrough</s> + # <u>underline</u> + # + # For a complete reference see + # <http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>. + # + # strip: This setting is provided for compatibility with some broken + # clients that send markup even though it's not enabled on the + # server. Dunst will try to strip the markup but the parsing is + # simplistic so using this option outside of matching rules for + # specific applications *IS GREATLY DISCOURAGED*. + # + # no: Disable markup parsing, incoming notifications will be treated as + # plain text. Dunst will not advertise that it has the body-markup + # capability if this is set as a global setting. + # + # It's important to note that markup inside the format option will be parsed + # regardless of what this is set to. + markup = full + + # 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 + # %n progress value if set without any extra characters + # %% Literal % + # Markup is allowed + format = "<b>%s</b>\n%b" + + # Alignment of message text. + # Possible values are "left", "center" and "right". + alignment = left + + # Show age of message if message is older than show_age_threshold + # seconds. + # Set to -1 to disable. + show_age_threshold = 60 + + # Split notifications into multiple lines if they don't fit into + # geometry. + word_wrap = yes + + # When word_wrap is set to no, specify where to make an ellipsis in long lines. + # Possible values are "start", "middle" and "end". + ellipsize = middle + + # Ignore newlines '\n' in notifications. + ignore_newline = no + + # Stack together notifications with the same content + stack_duplicates = true + + # Hide the count of stacked notifications with the same content + hide_duplicate_count = false + + # Display indicators for URLs (U) and actions (A). + show_indicators = yes + + ### Icons ### + + # Align icons left/right/off + icon_position = off + + # Scale larger icons down to this size, set to 0 to disable + max_icon_size = 32 + + # Paths to default icons. + icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/ + + ### History ### + + # Should a notification popped up from history be sticky or timeout + # as if it would normally do. + sticky_history = yes + + # Maximum amount of notifications kept in history + history_length = 20 + + ### Misc/Advanced ### + + # dmenu path. + dmenu = /usr/bin/dmenu -p dunst: + + # Browser for opening urls in context menu. + browser = /usr/bin/brave -new-tab + + # Always run rule-defined scripts, even if the notification is suppressed + always_run_script = true + + # Define the title of the windows spawned by dunst + title = Dunst + + # Define the class of the windows spawned by dunst + class = Dunst + + # Print a notification on startup. + # This is mainly for error detection, since dbus (re-)starts dunst + # automatically after a crash. + startup_notification = false + + # Manage dunst's desire for talking + # Can be one of the following values: + # crit: Critical features. Dunst aborts + # warn: Only non-fatal warnings + # mesg: Important Messages + # info: all unimportant stuff + # debug: all less than unimportant stuff + verbosity = mesg + + # Define the corner radius of the notification window + # in pixel size. If the radius is 0, you have no rounded + # corners. + # The radius will be automatically lowered if it exceeds half of the + # notification height to avoid clipping text and/or icons. + corner_radius = 0 + + ### Legacy + + # Use the Xinerama extension instead of RandR for multi-monitor support. + # This setting is provided for compatibility with older nVidia drivers that + # do not support RandR and using it on systems that support RandR is highly + # discouraged. + # + # By enabling this setting dunst will not be able to detect when a monitor + # is connected or disconnected which might break follow mode if the screen + # layout changes. + force_xinerama = false + + ### mouse + + # Defines action of mouse event + # Possible values are: + # * none: Don't do anything. + # * do_action: If the notification has exactly one action, or one is marked as default, + # invoke it. If there are multiple and no default, open the context menu. + # * close_current: Close current notification. + # * close_all: Close all notifications. + mouse_left_click = close_current + mouse_middle_click = do_action + mouse_right_click = close_all + +# Experimental features that may or may not work correctly. Do not expect them +# to have a consistent behaviour across releases. +[experimental] + # Calculate the dpi to use on a per-monitor basis. + # If this setting is enabled the Xft.dpi value will be ignored and instead + # dunst will attempt to calculate an appropriate dpi value for each monitor + # using the resolution and physical size. This might be useful in setups + # where there are multiple screens with very different dpi values. + per_monitor_dpi = false + +[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". Make sure this key actually exists on your keyboard layout, + # e.g. check output of 'xmodmap -pke' + 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 = "#191919" + foreground = "#999999" + timeout = 10 + # Icon for notifications with low urgency, uncomment to enable + #icon = /path/to/icon + +[urgency_normal] + background = "#191919" + foreground = "#ffffff" + timeout = 10 + # Icon for notifications with normal urgency, uncomment to enable + #icon = /path/to/icon + +[urgency_critical] + background = "#fd4d4d" + foreground = "#ffffff" + timeout = 0 + # Icon for notifications with critical urgency, uncomment to enable + #icon = /path/to/icon + +# 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 (discouraged, see desktop_entry) +# body +# category +# desktop_entry +# icon +# match_transient +# msg_urgency +# stack_tag +# summary +# +# and you can override the +# background +# foreground +# format +# frame_color +# fullscreen +# new_icon +# set_stack_tag +# set_transient +# timeout +# urgency +# +# Shell-like globbing will get expanded. +# +# Instead of the appname filter, it's recommended to use the desktop_entry filter. +# GLib based applications export their desktop-entry name. In comparison to the appname, +# the desktop-entry won't get localized. +# +# 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. + +# Disable the transient hint so that idle_threshold cannot be bypassed from the +# client +#[transient_disable] +# match_transient = yes +# set_transient = no +# +# Make the handling of transient notifications more strict by making them not +# be placed in history. +#[transient_history_ignore] +# match_transient = yes +# history_ignore = yes + +# fullscreen values +# show: show the notifications, regardless if there is a fullscreen window opened +# delay: displays the new notification, if there is no fullscreen window active +# If the notification is already drawn, it won't get undrawn. +# pushback: same as delay, but when switching into fullscreen, the notification will get +# withdrawn from screen again and will get delayed like a new notification +#[fullscreen_delay_everything] +# fullscreen = delay +#[fullscreen_show_critical] +# msg_urgency = critical +# fullscreen = show + +#[espeak] +# summary = "*" +# script = dunst_espeak.sh + +#[script-test] +# summary = "*script*" +# script = dunst_test.sh + +#[ignore] +# # This notification will not be displayed +# summary = "foobar" +# format = "" + +#[history-ignore] +# # This notification will not be saved in history +# summary = "foobar" +# history_ignore = yes + +#[skip-display] +# # This notification will not be displayed, but will be included in the history +# summary = "foobar" +# skip_display = yes + +#[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 +# +#[stack-volumes] +# appname = "some_volume_notifiers" +# set_stack_tag = "volume" +# +# vim: ft=cfg diff --git a/.config/mimeapps.list b/.config/mimeapps.list new file mode 100644 index 0000000..6c1db7b --- /dev/null +++ b/.config/mimeapps.list @@ -0,0 +1,222 @@ +[Default Applications] + +x-scheme-handler/lbry=lbry.desktop +x-scheme-handler/etcher=balena-etcher-electron.desktop +x-scheme-handler/http=org.qutebrowser.qutebrowser.desktop +x-scheme-handler/https=org.qutebrowser.qutebrowser.desktop +x-scheme-handler/qute=org.qutebrowser.qutebrowser.desktop +x-scheme-handler/mailto=neomutt.desktop + +applications/pdf=org.pwmt.zathura.desktop +application/pdf=org.pwmt.zathura.desktop + +text/comma-separated-values=nvim.desktop +text/csv=nvim.desktop +text/english=nvim.desktop +text/google-video-pointer=nvim.desktop +text/html=nvim.desktop +text/mathml=nvim.desktop +text/plain=nvim.desktop +text/rtf=nvim.desktop +text/spreadsheet=nvim.desktop +text/tab-separated-values=nvim.desktop +text/vnd.trolltech.linguist=nvim.desktop +text/x-c=nvim.desktop +text/x-c++=nvim.desktop +text/x-c++hdr=nvim.desktop +text/x-chdr=nvim.desktop +text/x-comma-separated-values=nvim.desktop +text/x-c++src=nvim.desktop +text/x-csrc=nvim.desktop +text/x-csv=nvim.desktop +text/x-google-video-pointer=nvim.desktop +text/x-java=nvim.desktop +text/x-makefile=nvim.desktop +text/xml=nvim.desktop +text/x-moc=nvim.desktop +text/x-pascal=nvim.desktop +text/x-tcl=nvim.desktop +text/x-tex=nvim.desktop + +audio/3gpp=mpv.desktop +audio/3gpp2=mpv.desktop +audio/aac=mpv.desktop +audio/ac3=mpv.desktop +audio/aiff=mpv.desktop +audio/AMR=mpv.desktop +audio/amr-wb=mpv.desktop +audio/AMR-WB=mpv.desktop +audio/basic=mpv.desktop +audio/dv=mpv.desktop +audio/eac3=mpv.desktop +audio/flac=mpv.desktop +audio/m3u=mpv.desktop +audio/m4a=mpv.desktop +audio/midi=mpv.desktop +audio/mp1=mpv.desktop +audio/mp2=mpv.desktop +audio/mp3=mpv.desktop +audio/mp4=mpv.desktop +audio/mpeg=mpv.desktop +audio/mpeg2=mpv.desktop +audio/mpeg3=mpv.desktop +audio/mpegurl=mpv.desktop +audio/mpg=mpv.desktop +audio/musepack=mpv.desktop +audio/ogg=mpv.desktop +audio/opus=mpv.desktop +audio/rn-mpeg=mpv.desktop +audio/scpls=mpv.desktop +audio/vnd.dolby.heaac.1=mpv.desktop +audio/vnd.dolby.heaac.2=mpv.desktop +audio/vnd.dolby.mlp=mpv.desktop +audio/vnd.dts=mpv.desktop +audio/vnd.dts.hd=mpv.desktop +audio/vnd.rn-realaudio=mpv.desktop +audio/vorbis=mpv.desktop +audio/wav=mpv.desktop +audio/webm=mpv.desktop +audio/x-aac=mpv.desktop +audio/x-adpcm=mpv.desktop +audio/x-aiff=mpv.desktop +audio/x-ape=mpv.desktop +audio/x-flac=mpv.desktop +audio/x-gsm=mpv.desktop +audio/x-gtp=mpv.desktop +audio/x-it=mpv.desktop +audio/x-m4a=mpv.desktop +audio/x-matroska=mpv.desktop +audio/x-mod=mpv.desktop +audio/x-mp1=mpv.desktop +audio/x-mp2=mpv.desktop +audio/x-mp3=mpv.desktop +audio/x-mpeg=mpv.desktop +audio/x-mpegurl=mpv.desktop +audio/x-mpg=mpv.desktop +audio/x-ms-asf=mpv.desktop +audio/x-ms-asx=mpv.desktop +audio/x-ms-wax=mpv.desktop +audio/x-ms-wma=mpv.desktop +audio/x-musepack=mpv.desktop +audio/x-pls=mpv.desktop +audio/x-pn-aiff=mpv.desktop +audio/x-pn-au=mpv.desktop +audio/x-pn-realaudio=mpv.desktop +audio/x-pn-realaudio-plugin=mpv.desktop +audio/x-pn-wav=mpv.desktop +audio/x-pn-windows-acm=mpv.desktop +audio/x-pn-windows-pcm=mpv.desktop +audio/x-ptb=mpv.desktop +audio/x-real-audio=mpv.desktop +audio/x-realaudio=mpv.desktop +audio/x-s3m=mpv.desktop +audio/x-scpls=mpv.desktop +audio/x-shorten=mpv.desktop +audio/x-speex=mpv.desktop +audio/x-tta=mpv.desktop +audio/x-vorbis=mpv.desktop +audio/x-vorbis+ogg=mpv.desktop +audio/x-wav=mpv.desktop +audio/x-wavpack=mpv.desktop +audio/x-xm=mpv.desktop + + +image/x-minolta-mrw=sxiv.desktop +image/webp=sxiv.desktop +image/vnd.ms-modi=sxiv.desktop +image/cgm=sxiv.desktop +image/vnd.rn-realpix=sxiv.desktop +image/x-pentax-pef=sxiv.desktop +image/vnd.dwg=sxiv.desktop +image/x-pict=sxiv.desktop +image/g3fax=sxiv.desktop +image/dpx=sxiv.desktop +image/x-kodak-kdc=sxiv.desktop +image/x-pic=sxiv.desktop +image/x-gimp-gih=sxiv.desktop +image/avif=sxiv.desktop +image/x-adobe-dng=sxiv.desktop +image/x-3ds=sxiv.desktop +image/x-kodak-dcr=sxiv.desktop +image/svg+xml-compressed=sxiv.desktop +image/x-panasonic-rw=sxiv.desktop +image/x-portable-bitmap=sxiv.desktop +image/ief=sxiv.desktop +image/tiff=sxiv.desktop +image/astc=sxiv.desktop +image/vnd.microsoft.icon=sxiv.desktop +image/jpm=sxiv.desktop +image/x-quicktime=sxiv.desktop +image/x-jp2-codestream=sxiv.desktop +image/x-icns=sxiv.desktop +image/x-applix-graphics=sxiv.desktop +image/x-kodak-k25=sxiv.desktop +image/x-sony-srf=sxiv.desktop +image/x-xfig=sxiv.desktop +image/x-xwindowdump=sxiv.desktop +image/jpx=sxiv.desktop +image/x-tiff-multipage=sxiv.desktop +image/heif=sxiv.desktop +image/x-canon-cr2=sxiv.desktop +image/x-macpaint=sxiv.desktop +image/x-skencil=sxiv.desktop +image/x-portable-anymap=sxiv.desktop +image/x-jng=sxiv.desktop +image/x-dib=sxiv.desktop +image/x-rgb=sxiv.desktop +image/x-sigma-x3f=sxiv.desktop +image/x-xbitmap=sxiv.desktop +image/x-sun-raster=sxiv.desktop +image/x-eps=sxiv.desktop +image/x-gimp-gbr=sxiv.desktop +image/x-gzeps=sxiv.desktop +image/x-sony-arw=sxiv.desktop +image/png=sxiv.desktop +image/wmf=sxiv.desktop +image/fits=sxiv.desktop +image/jpeg=sxiv.desktop +image/vnd.dxf=sxiv.desktop +image/emf=sxiv.desktop +image/x-photo-cd=sxiv.desktop +image/x-cmu-raster=sxiv.desktop +image/x-olympus-orf=sxiv.desktop +image/vnd.djvu=sxiv.desktop +image/x-fuji-raf=sxiv.desktop +image/x-kde-raw=sxiv.desktop +image/x-portable-pixmap=sxiv.desktop +image/vnd.zbrush.pcx=sxiv.desktop +image/x-lwo=sxiv.desktop +image/x-win-bitmap=sxiv.desktop +image/x-exr=sxiv.desktop +image/ktx=sxiv.desktop +image/x-niff=sxiv.desktop +image/x-xcf=sxiv.desktop +image/x-lws=sxiv.desktop +image/x-xcursor=sxiv.desktop +image/x-sgi=sxiv.desktop +image/x-msod=sxiv.desktop +image/x-fpx=sxiv.desktop +image/vnd.adobe.photoshop=sxiv.desktop +image/x-panasonic-rw2=sxiv.desktop +image/x-nikon-nef=sxiv.desktop +image/vnd.wap.wbmp=sxiv.desktop +image/x-compressed-xcf=sxiv.desktop +image/x-hdr=sxiv.desktop +image/x-portable-graymap=sxiv.desktop +image/jp2=sxiv.desktop +image/x-bzeps=sxiv.desktop +image/svg+xml=sxiv.desktop +image/rle=sxiv.desktop +image/gif=sxiv.desktop +image/x-dcraw=sxiv.desktop +image/ktx2=sxiv.desktop +image/x-tga=sxiv.desktop +image/vnd.djvu+multipage=sxiv.desktop +image/x-dds=sxiv.desktop +image/x-sony-sr2=sxiv.desktop +image/openraster=sxiv.desktop +image/x-canon-crw=sxiv.desktop +image/x-ilbm=sxiv.desktop +image/x-xpixmap=sxiv.desktop +image/x-gimp-pat=sxiv.desktop +image/bmp=sxiv.desktop diff --git a/.config/mutt/muttrc b/.config/mutt/muttrc new file mode 100644 index 0000000..8896639 --- /dev/null +++ b/.config/mutt/muttrc @@ -0,0 +1,31 @@ +# vim: filetype=neomuttrc +source /usr/share/mutt-wizard/mutt-wizard.muttrc + +set editor = "nvim" +auto_view text/html + +color hdrdefault green black +color quoted blue black +color signature blue black +color attachment red black +color prompt brightmagenta black +color message brightred black +color error brightred black +color indicator black green +color status color234 green +color tree brightblack black +color normal white black +color markers green black +color search white black +color tilde brightmagenta black +color index blue black ~F +color index green black "~N|~O" + +bind pager j next-line +bind pager k previous-line +bind attach,index,pager \CD next-page +bind attach,index,pager \CU previous-page +bind pager G bottom +bind attach,index G last-entry +bind pager l next-undeleted +bind pager h previous-undeleted diff --git a/.config/nvim/.netrwhist b/.config/nvim/.netrwhist new file mode 100644 index 0000000..a27bcca --- /dev/null +++ b/.config/nvim/.netrwhist @@ -0,0 +1,12 @@ +let g:netrw_dirhistmax =10 +let g:netrw_dirhistcnt =1 +let g:netrw_dirhist_1='/home/nathanreiner/Dokumente/ctf' +let g:netrw_dirhist_0='/home/nathanreiner/Dokumente/ctf/misc' +let g:netrw_dirhist_9='/home/nathanreiner/Dokumente/ctf' +let g:netrw_dirhist_8='/home/nathanreiner' +let g:netrw_dirhist_7='/home/nathanreiner/.config/nvim' +let g:netrw_dirhist_6='/home/nathanreiner' +let g:netrw_dirhist_5='/home/nathanreiner/.config/nvim' +let g:netrw_dirhist_4='/home/nathanreiner' +let g:netrw_dirhist_3='/home/nathanreiner/Dokumente/C++' +let g:netrw_dirhist_2='/home/nathanreiner/Dokumente' diff --git a/.config/nvim/README.md b/.config/nvim/README.md new file mode 100644 index 0000000..12b2670 --- /dev/null +++ b/.config/nvim/README.md @@ -0,0 +1,3 @@ +# NeoVim Configuration + +My NeoVim Configuration diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua new file mode 100644 index 0000000..95f4370 --- /dev/null +++ b/.config/nvim/init.lua @@ -0,0 +1,10 @@ +-- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +-- ┃ ┏┓╻┏━┓ ╻ ╻╻┏┳┓ ┏━╸┏━┓┏┓╻┏━╸╻┏━╸╻ ╻┏━┓┏━┓╺┳╸╻┏━┓┏┓╻ ┃ +-- ┃ ┃┗┫┣━┫ ┃┏┛┃┃┃┃ ┃ ┃ ┃┃┗┫┣╸ ┃┃╺┓┃ ┃┣┳┛┣━┫ ┃ ┃┃ ┃┃┗┫ ┃ +-- ┃ ╹ ╹┗━┛ ┗┛ ╹╹ ╹ ┗━╸┗━┛╹ ╹╹ ╹┗━┛┗━┛╹┗╸╹ ╹ ╹ ╹┗━┛╹ ╹ ┃ +-- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +require('plug') +require('keymap') +require('settings') +require('lsp') diff --git a/.config/nvim/lua/keymap.lua b/.config/nvim/lua/keymap.lua new file mode 100644 index 0000000..200c5cb --- /dev/null +++ b/.config/nvim/lua/keymap.lua @@ -0,0 +1,55 @@ +-- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +-- ┃ ┏┓╻┏━┓ ╻ ╻╻┏┳┓ ╻┏ ┏━╸╻ ╻┏┳┓┏━┓┏━┓┏━┓ ┃ +-- ┃ ┃┗┫┣━┫ ┃┏┛┃┃┃┃ ┣┻┓┣╸ ┗┳┛┃┃┃┣━┫┣━┛┗━┓ ┃ +-- ┃ ╹ ╹┗━┛ ┗┛ ╹╹ ╹ ╹ ╹┗━╸ ╹ ╹ ╹╹ ╹╹ ┗━┛ ┃ +-- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +vim.g.mapleader = ',' +vim.api.nvim_set_keymap("n", "<leader>C", "<leader>c<space>", {}) +vim.api.nvim_set_keymap("n", "<leader>n", "<C-w>n", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>sc", ":so $MYVIMRC<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>ec", ":e $MYVIMRC<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>tn", ":tabnew<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>tc", ":tabclose<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>tt", ":tabnew<CR>:term<CR>a", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>Tn", ":tnext<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>Tp", ":tprevious<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>Tf", ":tfirst<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>Tl", ":tlast<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>tselect", ":tselect<Space>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>Tc", ":!ctags -R --sort=yes --c++-kins=+p --filed=+iaS --extras=+q . <CR><CR>", {noremap = true}) + +vim.api.nvim_set_keymap("n", "<leader>f", ":Lexplore<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>F", ":Lexplore %:p:h<CR>", {noremap = true}) + +vim.api.nvim_set_keymap("n", "<leader>zt", ":VZKFindTag<CR>", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>zh", ":VZKHistory<CR>", {noremap = true}) + +vim.api.nvim_set_keymap("i", "(", "()<Esc>i", {noremap = true}) +vim.api.nvim_set_keymap("i", ")", "<ESC>:call brackify#putbracket(')')<CR>la", {noremap = true}) +vim.api.nvim_set_keymap("i", "[", "[]<Esc>i", {noremap = true}) +vim.api.nvim_set_keymap("i", "]", "<ESC>:call brackify#putbracket(']')<CR>la", {noremap = true}) +vim.api.nvim_set_keymap("i", "{", "{}<Esc>i", {noremap = true}) +vim.api.nvim_set_keymap("i", "}", "<ESC>:call brackify#putbracket('}')<CR>la", {noremap = true}) +vim.cmd('autocmd Filetype html inoremap <buffer> < <><ESC>i') +vim.cmd('autocmd Filetype html inoremap <buffer> > <ESC>:call brackify#putbracket(">")<CR>la') + +vim.api.nvim_set_keymap("i", '"', "<ESC>:call brackify#putquotes('\"')<CR>a", {noremap = true}) +vim.api.nvim_set_keymap("i", "'", "<ESC>:call brackify#putquotes(\"'\")<CR>a", {noremap = true}) + +vim.api.nvim_set_keymap("v", "(", "s(<ESC>pa)", {noremap = true}) +vim.api.nvim_set_keymap("v", "[", "s[<ESC>pa]", {noremap = true}) +vim.api.nvim_set_keymap("v", "{", "s{<ESC>pa}", {noremap = true}) +vim.api.nvim_set_keymap("v", '"', 's"<ESC>pa"', {noremap = true}) +vim.api.nvim_set_keymap("v", "'", "s'<ESC>pa'", {noremap = true}) +vim.cmd('autocmd Filetype c nnoremap <buffer> <tab> :CSelNextArg<Cr>') + +vim.api.nvim_set_keymap("n", "<leader>dB", ":GdbBreakpointToggle", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>dn", ":GdbNext", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>ds", ":GdbStep", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>dr", ":GdbRun", {noremap = true}) +vim.api.nvim_set_keymap("n", "<leader>dc", ":GdbContinue", {noremap = true}) +vim.cmd('inoremap <expr> <Tab> pumvisible() ? "\\<C-n>" : "\\<Tab>"') +vim.cmd('inoremap <expr> <S-Tab> pumvisible() ? "\\<C-p>" : "\\<S-Tab>"') + +vim.cmd("autocmd TextChangedI * lua vim.lsp.omnifunc(1)") diff --git a/.config/nvim/lua/lsp.lua b/.config/nvim/lua/lsp.lua new file mode 100644 index 0000000..41c9e16 --- /dev/null +++ b/.config/nvim/lua/lsp.lua @@ -0,0 +1,46 @@ +local clangd_on_attach = function(client, bufnr) + + local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end + local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end + + -- Enable completion triggered by <c-x><c-o> + buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc') + + -- Mappings. + local opts = { noremap=true, silent=true } + -- See `:help vim.lsp.*` for documentation on any of the below functions + buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts) + buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts) + buf_set_keymap('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts) + buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts) + buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts) + buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts) + buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts) + buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts) + buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts) + buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts) + buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts) + buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts) + buf_set_keymap('n', '<space>e', '<cmd>lua vim.diagnostic.open_float()<CR>', opts) + buf_set_keymap('n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<CR>', opts) + buf_set_keymap('n', ']d', '<cmd>lua vim.diagnostic.goto_next()<CR>', opts) + buf_set_keymap('n', '<space>q', '<cmd>lua vim.diagnostic.setloclist()<CR>', opts) + buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts) + buf_set_keymap('i', '<c-space>', '<c-x><c-o>', opts) + buf_set_keymap('i', '::', '::<c-x><c-o>', opts) + buf_set_keymap('i', '.', '.<c-x><c-o>', opts) + buf_set_keymap('i', '->', '-><c-x><c-o>', opts) + buf_set_keymap('i', '<BS>', '<BS><c-x><c-o>', opts); +end + + +require('lspconfig').clangd.setup{ + cmd = { + "clangd", + "--enable-config", + "--background-index", + "--pch-storage=memory", + "--all-scopes-completion", + }, + on_attach = clangd_on_attach, +} diff --git a/.config/nvim/lua/plug.lua b/.config/nvim/lua/plug.lua new file mode 100644 index 0000000..43c036e --- /dev/null +++ b/.config/nvim/lua/plug.lua @@ -0,0 +1,22 @@ +-- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +-- ┃ ┏┓╻┏━┓ ╻ ╻╻┏┳┓ ┏━┓╻ ╻ ╻┏━╸╻┏┓╻┏━┓ ┃ +-- ┃ ┃┗┫┣━┫ ┃┏┛┃┃┃┃ ┣━┛┃ ┃ ┃┃╺┓┃┃┗┫┗━┓ ┃ +-- ┃ ╹ ╹┗━┛ ┗┛ ╹╹ ╹ ╹ ┗━╸┗━┛┗━┛╹╹ ╹┗━┛ ┃ +-- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +local Plug = vim.fn['plug#'] + +vim.call('plug#begin', '~/.local/share/nvim/plugged') + +Plug 'airblade/vim-gitgutter' +Plug 'tpope/vim-fugitive' +Plug 'scrooloose/nerdcommenter' +Plug 'neovim/nvim-lspconfig' +Plug 'NPScript/vim-zettelkasten' +Plug 'sakhnik/nvim-gdb' +Plug 'nvim-treesitter/nvim-treesitter' +Plug 'jalvesaq/Nvim-R' + +vim.call('plug#end') + + diff --git a/.config/nvim/lua/settings.lua b/.config/nvim/lua/settings.lua new file mode 100644 index 0000000..c3bd598 --- /dev/null +++ b/.config/nvim/lua/settings.lua @@ -0,0 +1,45 @@ +-- ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +-- ┃ ┏┓╻┏━┓ ╻ ╻╻┏┳┓ ┏━┓┏━╸╺┳╸╺┳╸╻┏┓╻┏━╸┏━┓ ┃ +-- ┃ ┃┗┫┣━┫ ┃┏┛┃┃┃┃ ┗━┓┣╸ ┃ ┃ ┃┃┗┫┃╺┓┗━┓ ┃ +-- ┃ ╹ ╹┗━┛ ┗┛ ╹╹ ╹ ┗━┛┗━╸ ╹ ╹ ╹╹ ╹┗━┛┗━┛ ┃ +-- ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +vim.opt.shell = '/usr/bin/zsh' +vim.opt.path = "**" + +vim.cmd('colorscheme n8') + +vim.opt.mouse = 'a' +vim.opt.tabstop = 2 +vim.opt.softtabstop = 0 +vim.opt.expandtab = false +vim.opt.shiftwidth = 2 +vim.opt.smartindent = true +vim.opt.list = true; +vim.opt.listchars = { tab= '> ', eol = '¬', trail = '·', nbsp = '•'} +vim.opt.splitright = true +vim.opt.splitbelow = false +vim.opt.laststatus = 2 +vim.opt.clipboard = "unnamedplus" +vim.opt.number = true +vim.opt.showcmd = true +vim.opt.wildmenu = true +vim.opt.hlsearch = true +vim.opt.conceallevel = 2 +vim.opt.colorcolumn = "80" +vim.cmd('filetype indent on') + +vim.opt.completeopt = { "menuone", "menu", "longest", "preview", "noinsert", "noselect" } + +vim.opt.spelllang = { "de_ch", "en_us" } + +vim.g.netrw_liststyle = 3 +vim.g.netrw_banner = false +vim.g.netrw_browser_split = 2 +vim.g.netrw_keepdir = true +vim.g.netrw_winsize = 30 +vim.cmd('autocmd Filetype netrw setlocal nolist') + +vim.g.airline_powerline_fonts = true + +vim.cmd("call statusbar#refresh()") diff --git a/.config/qutebrowser/autoconfig.yml b/.config/qutebrowser/autoconfig.yml new file mode 100644 index 0000000..f03d42d --- /dev/null +++ b/.config/qutebrowser/autoconfig.yml @@ -0,0 +1,138 @@ +# If a config.py file exists, this file is ignored unless it's explicitly loaded +# via config.load_autoconfig(). For more information, see: +# https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#loading-autoconfigyml +# DO NOT edit this file by hand, qutebrowser will overwrite it. +# Instead, create a config.py - see :help for details. + +config_version: 2 +settings: + bindings.commands: + global: + normal: + gle: spawn --userscript leo-eng + glf: spawn --userscript leo-fr + colors.completion.category.bg: + global: '#252525' + colors.completion.category.border.bottom: + global: '#252525' + colors.completion.category.border.top: + global: '#252525' + colors.completion.even.bg: + global: '#181818' + colors.completion.item.selected.bg: + global: '#547f62' + colors.completion.item.selected.border.bottom: + global: '#547f62' + colors.completion.item.selected.border.top: + global: '#547f62' + colors.completion.item.selected.match.fg: + global: '#ffffff' + colors.completion.match.fg: + global: '#547f62' + colors.completion.odd.bg: + global: '#181818' + colors.completion.scrollbar.bg: + global: '#252525' + colors.completion.scrollbar.fg: + global: '#547f62' + colors.messages.error.bg: + global: '#bc5858' + colors.messages.info.bg: + global: '#181818' + colors.messages.warning.bg: + global: '#e8c75c' + colors.statusbar.command.bg: + global: '#181818' + colors.statusbar.insert.bg: + global: '#252525' + colors.statusbar.normal.bg: + global: '#181818' + colors.statusbar.progress.bg: + global: '#547f62' + colors.statusbar.url.error.fg: + global: '#bc5858' + colors.statusbar.url.hover.fg: + global: '#6da6ce' + colors.statusbar.url.success.http.fg: + global: '#989898' + colors.statusbar.url.success.https.fg: + global: '#547f62' + colors.tabs.bar.bg: + global: '#181818' + colors.tabs.even.bg: + global: '#181818' + colors.tabs.even.fg: + global: '#999' + colors.tabs.indicator.error: + global: '#e8c75c' + colors.tabs.indicator.start: + global: '#6da6ce' + colors.tabs.indicator.stop: + global: '#547f62' + colors.tabs.odd.bg: + global: '#181818' + colors.tabs.odd.fg: + global: '#999' + colors.tabs.selected.even.bg: + global: '#547f62' + colors.tabs.selected.odd.bg: + global: '#547f62' + colors.webpage.darkmode.algorithm: + global: lightness-cielab + colors.webpage.darkmode.enabled: + global: false + colors.webpage.darkmode.grayscale.all: + global: true + colors.webpage.darkmode.grayscale.images: + global: 0.5 + colors.webpage.darkmode.policy.images: + global: never + colors.webpage.darkmode.policy.page: + global: always + colors.webpage.darkmode.threshold.background: + global: 24 + content.blocking.enabled: + global: true + content.blocking.method: + global: adblock + content.notifications.enabled: + https://www.chess.com: false + https://www.reddit.com: false + downloads.location.directory: + global: ~/Downloads + qt.force_platformtheme: + global: null + qt.workarounds.locale: + global: true + qt.workarounds.remove_service_workers: + global: false + statusbar.padding: + global: + bottom: 5 + left: 5 + right: 5 + top: 5 + statusbar.position: + global: bottom + tabs.indicator.padding: + global: + bottom: 4 + left: 2 + right: 5 + top: 4 + tabs.indicator.width: + global: 9 + tabs.max_width: + global: 250 + tabs.padding: + global: + bottom: 5 + left: 5 + right: 5 + top: 5 + tabs.position: + global: top + url.default_page: + global: ~/.config/startpage/start.html + url.start_pages: + global: ~/.config/startpage/start.html diff --git a/.config/sc-im/scimrc b/.config/sc-im/scimrc new file mode 100644 index 0000000..13761e4 --- /dev/null +++ b/.config/sc-im/scimrc @@ -0,0 +1,10 @@ +set autocalc +set numeric +set numeric_decimal=0 +set overlap +set xlsx_readformulas + +color "type=HEADINGS bold=0 fg=BLACK bg=GREEN" +color "type=HEADINGS_ODD bold=0 fg=BLACK bg=GREEN" +color "type=CELL_SELECTION bold=0 fg=BLACK bg=GREEN" +color "type=CELL_SELECTION_SC bold=0 fg=BLACK bg=BLUE" diff --git a/.config/sway/config b/.config/sway/config new file mode 100644 index 0000000..3621a50 --- /dev/null +++ b/.config/sway/config @@ -0,0 +1,287 @@ +# Default config for sway +# +# Copy this to ~/.config/sway/config and edit it to your liking. +# +# Read `man 5 sway` for a complete reference. + +### Variables +# +# Logo key. Use Mod1 for Alt. +set $mod Mod4 +# Home row direction keys, like vim +set $left h +set $down j +set $up k +set $right l +# Your preferred terminal emulator +set $term st +# Your preferred application launcher +# Note: pass the final command to swaymsg so that the resulting window can be opened +# on the original workspace that the command was run on. +set $menu dmenu_path | dmenu | xargs swaymsg exec -- + +set $browser qutebrowser +set $cmus st -e cmus +set $mail st -e neomutt +set $pass passmenu --type +set $filebrowser st -e luis + +set $lock swaylock -c 181818 \ + --font "Source Code Pro" \ + --inside-color 181818 \ + --inside-ver-color 181818 \ + --ring-color 547f62 \ + --ring-ver-color 547f62 \ + --separator-color 547f62 \ + --text-color ffffff \ + --text-ver-color ffffff \ + -r -K + +### Output configuration +# +# Default wallpaper (more resolutions are available in /usr/share/backgrounds/sway/) + +output * bg /home/nathanreiner/Bilder/bg.png fill +# +# Example configuration: +# +# output HDMI-A-1 resolution 1920x1080 position 1920,0 +# +# You can get the names of your outputs by running: swaymsg -t get_outputs + +output VGA-1 transform 270 +output VGA-1 pos 0 0 +output HDMI-1 pos 1600 0 +output VGA-1 mode --custom 1600x900@59.946Hz + +default_border pixel 2 + +# Set colors +set $bg #181818 +set $fg #eff1f5 +set $br #547f62 +set $ia #232425 + + +# class border backgr. text indicator child_border +client.focused $br $br $fg $bg $br +client.focused_inactive $bg $bg $fg $bg $bg +client.unfocused $bg $bg $fg $bg $bg +#client.urgent $br $br $fg $bg $br #currently not implemented +#client.placeholder $br $br $fg $bg $br #currently not implemented +client.background $bg + +### Idle configuration +# +# Example configuration: +# +exec swayidle -w \ + timeout 300 'swaylock -f -c 000000' \ + timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ + before-sleep 'swaylock -f -c 000000' +# +# This will lock your screen after 300 seconds of inactivity, then turn off +# your displays after another 300 seconds, and turn your screens back on when +# resumed. It will also lock your screen before your computer goes to sleep. + +### Input configuration +# +# Example configuration: +# +# input "2:14:SynPS/2_Synaptics_TouchPad" { +# dwt enabled +# tap enabled +# natural_scroll enabled +# middle_emulation enabled +# } +# +# You can get the names of your inputs by running: swaymsg -t get_inputs +# Read `man 5 sway-input` for more information about this section. + +input "9494:66:Cooler_Master_Technology_Inc._MasterKeys_Lite_L_Combo_Keyboard(KB)" { + # Keyboard + xkb_layout "us,ch" + xkb_variant ",de_mac" + xkb_options "caps:escape,grp:win_space_toggle" +} + +input "1133:49257:Logitech_USB_Laser_Mouse" { + natural_scroll "enabled" +} + +### Key bindings +# +# Basics: +# + bindsym $mod+Shift+Return exec $term + bindsym $mod+Shift+b exec $browser + bindsym $mod+Shift+m exec $cmus + bindsym $mod+Shift+e exec $mail + bindsym $mod+Escape exec $lock + bindsym $mod+Shift+p exec $pass + bindsym $mod+Shift+f exec $filebrowser + + bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5% + bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% + bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle + bindsym XF86AudioMicMute exec pactl set-source-mute @DEFAULT_SOURCE@ toggle + bindsym XF86MonBrightnessDown exec brightnessctl set 5%- + bindsym XF86MonBrightnessUp exec brightnessctl set +5% + bindsym XF86AudioPlay exec playerctl play-pause + bindsym XF86AudioNext exec playerctl next + bindsym XF86AudioPrev exec playerctl previous + + # Kill focused window + bindsym $mod+Shift+c kill + + # Start your launcher + bindsym $mod+p exec $menu + + # Drag floating windows by holding down $mod and left mouse button. + # Resize them with right mouse button + $mod. + # Despite the name, also works for non-floating windows. + # Change normal to inverse to use left mouse button for resizing and right + # mouse button for dragging. + floating_modifier $mod normal + + # Reload the configuration file + bindsym $mod+c reload + + # Exit sway (logs you out of your Wayland session) + bindsym $mod+Shift+q exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit' +# +# Moving around: +# + # Move your focus around + bindsym $mod+$left focus left + bindsym $mod+$down focus down + bindsym $mod+$up focus up + bindsym $mod+$right focus right + # Or use $mod+[up|down|left|right] + bindsym $mod+Left focus left + bindsym $mod+Down focus down + bindsym $mod+Up focus up + bindsym $mod+Right focus right + + # Move the focused window with the same, but add Shift + bindsym $mod+Shift+$left move left + bindsym $mod+Shift+$down move down + bindsym $mod+Shift+$up move up + bindsym $mod+Shift+$right move right + # Ditto, with arrow keys + bindsym $mod+Shift+Left move left + bindsym $mod+Shift+Down move down + bindsym $mod+Shift+Up move up + bindsym $mod+Shift+Right move right +# +# Workspaces: +# + # Switch to workspace + bindsym $mod+1 workspace number 1 + bindsym $mod+2 workspace number 2 + bindsym $mod+3 workspace number 3 + bindsym $mod+4 workspace number 4 + bindsym $mod+5 workspace number 5 + bindsym $mod+6 workspace number 6 + bindsym $mod+7 workspace number 7 + bindsym $mod+8 workspace number 8 + bindsym $mod+9 workspace number 9 + bindsym $mod+0 workspace number 10 + # Move focused container to workspace + bindsym $mod+Shift+1 move container to workspace number 1 + bindsym $mod+Shift+2 move container to workspace number 2 + bindsym $mod+Shift+3 move container to workspace number 3 + bindsym $mod+Shift+4 move container to workspace number 4 + bindsym $mod+Shift+5 move container to workspace number 5 + bindsym $mod+Shift+6 move container to workspace number 6 + bindsym $mod+Shift+7 move container to workspace number 7 + bindsym $mod+Shift+8 move container to workspace number 8 + bindsym $mod+Shift+9 move container to workspace number 9 + bindsym $mod+Shift+0 move container to workspace number 10 + # Note: workspaces can have any name you want, not just numbers. + # We just use 1-10 as the default. +# +# Layout stuff: +# + # You can "split" the current object of your focus with + # $mod+b or $mod+v, for horizontal and vertical splits + # respectively. + bindsym $mod+b splith + bindsym $mod+v splitv + + # Switch the current container between different layout styles + bindsym $mod+s layout stacking + bindsym $mod+w layout tabbed + bindsym $mod+e layout toggle split + + # Make the current focus fullscreen + bindsym $mod+f fullscreen + + # Toggle the current focus between tiling and floating mode + bindsym $mod+Shift+space floating toggle + + # Swap focus between the tiling area and the floating area + bindsym $mod+space focus mode_toggle + + # Move focus to the parent container + bindsym $mod+a focus parent +# +# Scratchpad: +# + # Sway has a "scratchpad", which is a bag of holding for windows. + # You can send windows there and get them back later. + + # Move the currently focused window to the scratchpad + bindsym $mod+Shift+minus move scratchpad + + # Show the next scratchpad window or hide the focused scratchpad window. + # If there are multiple scratchpad windows, this command cycles through them. + bindsym $mod+minus scratchpad show +# +# Resizing containers: +# +mode "resize" { + # left will shrink the containers width + # right will grow the containers width + # up will shrink the containers height + # down will grow the containers height + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px + + # Ditto, with arrow keys + bindsym Left resize shrink width 10px + bindsym Down resize grow height 10px + bindsym Up resize shrink height 10px + bindsym Right resize grow width 10px + + # Return to default mode + bindsym Return mode "default" + bindsym Escape mode "default" +} +bindsym $mod+r mode "resize" + +# +# Status Bar: +# +# Read `man 5 sway-bar` for more information about this section. +# bar { +# position top +# +# # When the status_command prints a new line to stdout, swaybar updates. +# # The default just shows the current date and time. +# status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done +# +# colors { +# statusline #ffffff +# background #323232 +# inactive_workspace #32323200 #32323200 #5c5c5c +# } +# } + +exec waybar + +include /etc/sway/config.d/* + +exec sprogs diff --git a/.config/waybar/config b/.config/waybar/config new file mode 100644 index 0000000..9743a0f --- /dev/null +++ b/.config/waybar/config @@ -0,0 +1,160 @@ +{ + // "layer": "top", // Waybar at top layer + // "position": "bottom", // Waybar position (top|bottom|left|right) + "height": 30, // Waybar height (to be removed for auto height) + // "width": 1280, // Waybar width + "spacing": 4, // Gaps between modules (4px) + // Choose the order of the modules + "modules-left": ["sway/workspaces"], + "modules-center": ["sway/window"], + "modules-right": ["pulseaudio", "network", "sway/language", "clock", "tray"], + // Modules configuration + // "sway/workspaces": { + // "disable-scroll": true, + // "all-outputs": true, + // "format": "{name}: {icon}", + // "format-icons": { + // "1": "", + // "2": "", + // "3": "", + // "4": "", + // "5": "", + // "urgent": "", + // "focused": "", + // "default": "" + // } + // }, + "keyboard-state": { + "numlock": true, + "capslock": true, + "format": "{name} {icon}", + "format-icons": { + "locked": "", + "unlocked": "" + } + }, + "sway/mode": { + "format": "<span style=\"italic\">{}</span>" + }, + "mpd": { + "format": "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} - {album} - {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) ⸨{songPosition}|{queueLength}⸩ {volume}% ", + "format-disconnected": "Disconnected ", + "format-stopped": "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped ", + "unknown-tag": "N/A", + "interval": 2, + "consume-icons": { + "on": " " + }, + "random-icons": { + "off": "<span color=\"#f53c3c\"></span> ", + "on": " " + }, + "repeat-icons": { + "on": " " + }, + "single-icons": { + "on": "1 " + }, + "state-icons": { + "paused": "", + "playing": "" + }, + "tooltip-format": "MPD (connected)", + "tooltip-format-disconnected": "MPD (disconnected)" + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + } + }, + "tray": { + // "icon-size": 21, + "spacing": 10 + }, + "clock": { + // "timezone": "America/New_York", + "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>", + "format-alt": "{:%Y-%m-%d}" + }, + "cpu": { + "format": "{usage}% ", + "tooltip": false + }, + "memory": { + "format": "{}% " + }, + "temperature": { + // "thermal-zone": 2, + // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", + "critical-threshold": 80, + // "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""] + }, + "backlight": { + // "device": "acpi_video1", + "format": "{percent}% {icon}", + "format-icons": ["", ""] + }, + "battery": { + "states": { + // "good": 95, + "warning": 30, + "critical": 15 + }, + "format": "{capacity}% {icon}", + "format-charging": "{capacity}% ", + "format-plugged": "{capacity}% ", + "format-alt": "{time} {icon}", + // "format-good": "", // An empty format will hide the module + // "format-full": "", + "format-icons": ["", "", "", "", ""] + }, + "battery#bat2": { + "bat": "BAT2" + }, + "network": { + // "interface": "wlp2*", // (Optional) To force the use of this interface + "format-wifi": "{essid} ({signalStrength}%) ", + "format-ethernet": "{ipaddr}/{cidr} ", + "tooltip-format": "{ifname} via {gwaddr} ", + "format-linked": "{ifname} (No IP) ", + "format-disconnected": "Disconnected ⚠", + "format-alt": "{ifname}: {ipaddr}/{cidr}" + }, + "pulseaudio": { + // "scroll-step": 1, // %, can be a float + "format": "{volume}% {icon}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}% ", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + }, + "on-click": "pavucontrol" + }, + "custom/media": { + "format": "{icon} {}", + "return-type": "json", + "max-length": 40, + "format-icons": { + "Playing": ">", + "Pause": "||", + "No players found": "0" + }, + "escape": true, + "exec": "playerctl status 2> /dev/null" // Script in resources folder + // "exec": "$HOME/.config/waybar/mediaplayer.py --player spotify 2> /dev/null" // Filter player based on name + }, +} + diff --git a/.config/waybar/style.css b/.config/waybar/style.css new file mode 100644 index 0000000..99be8d7 --- /dev/null +++ b/.config/waybar/style.css @@ -0,0 +1,127 @@ +* { + /* `otf-font-awesome` is required to be installed for icons */ + font-family: 'Source Code Pro', Roboto, Helvetica, Arial, sans-serif; + font-size: 13px; +} + +window#waybar { + background-color: #191919; + border-bottom: 3px solid #282828; + color: #ffffff; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +/* +window#waybar.empty { +background-color: transparent; +} +window#waybar.solo { +background-color: #FFFFFF; +} + */ + + window#waybar.termite { + background-color: #3F3F3F; + } + + window#waybar.chromium { + background-color: #000000; + border: none; + } + + #workspaces button { + padding: 0 5px; + background-color: transparent; + color: #ffffff; + /* Use box-shadow instead of border so the text isn't offset */ + box-shadow: inset 0 -3px transparent; + /* Avoid rounded borders under each workspace name */ + border: none; + border-radius: 0; + } + + /* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */ + #workspaces button:hover { + background: rgba(0, 0, 0, 0); + box-shadow: inset 0 -3px #547f62; + } + + #workspaces button.focused { + background-color: #547f62; + box-shadow: inset 0 -3px #547f62; + } + + #workspaces button.urgent { + background-color: #eb4d4b; + } + + #mode { + background-color: #64727D; + border-bottom: 3px solid #ffffff; + } + + #clock, + #battery, + #cpu, + #memory, + #disk, + #temperature, + #backlight, + #network, + #pulseaudio, + #custom-media, + #language, + #tray, + #mode, + #idle_inhibitor, + #mpd { + padding: 0 10px; + color: #ffffff; + background-color: #282828; + } + + #window, + #workspaces { + margin: 0 4px; + } + + /* If workspaces is the leftmost module, omit left margin */ + .modules-left > widget:first-child > #workspaces { + margin-left: 0; + } + + /* If workspaces is the rightmost module, omit right margin */ + .modules-right > widget:last-child > #workspaces { + margin-right: 0; + } + + @keyframes blink { + to { + background-color: #ffffff; + color: #000000; + } + } + + #battery.critical:not(.charging) { + background-color: #f53c3c; + color: #ffffff; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #tray > .passive { + -gtk-icon-effect: dim; + } + + #tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; + } diff --git a/.config/zsh/.zshrc b/.config/zsh/.zshrc new file mode 100644 index 0000000..7eb08bb --- /dev/null +++ b/.config/zsh/.zshrc @@ -0,0 +1,45 @@ +# Created by newuser for 5.8 + +autoload -Uz compinit promptinit + +# Completion +compinit +zstyle ':completion::complete:*' gain-privileges 1 +zstyle ':completion:*' menu select + +# Prompt +if [ "$TERM" != "linux" ]; then + fpath=(~/.config/zsh/ $fpath) + autoload -Uz knight + knight +else + PROMPT='%n @ %m %~ %# ' +fi; + +STATUS="Hallo" + +# Keybindings +bindkey -v + +# Aliases +alias ls='ls --color=auto' +alias la='ls -a --color=auto' +alias ll='ls -l --color=auto -h' +setopt COMPLETE_ALIASES + +# History +export HISTSIZE=1000 +export SAVEHIST=1000 +export HISTFILE=~/.zsh_history +setopt INC_APPEND_HISTORY +setopt HIST_IGNORE_DUPS + +# Syntax Highlight +source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh + +source ~/.profile + +# Auto CD +setopt AUTO_CD + +export HISTFILE="$XDG_CACHE_HOME"/zsh/history diff --git a/.config/zsh/knight b/.config/zsh/knight new file mode 100644 index 0000000..507212f --- /dev/null +++ b/.config/zsh/knight @@ -0,0 +1,17 @@ + +prompt_git_status() { + if [ -d ".git" ]; then + sign="\ue725"; + [[ -n $(git status -s) ]] && sign="\ufc1a"; + git_branch=$(git branch); + echo "%F{#547f62}%f%B%F{#191919}%K{#547f62} ${sign} ${git_branch##*\* } %f%k%b"; + fi; +} + +update_prompt() { + RPROMPT="$(prompt_git_status)" +} + +PROMPT="%B%F{#191919}%K{#547f62} %n %k%b%f%F{#547f62}%K{#191919}%f%k%F{#191919}%K{#547f62}%f%k%B%K{#547f62}%F{#191919} %m %b%k%f%F{#547f62}%K{#191919}%f%k%F{#191919}%K{#7698C4}%B %~ %b%f%k%F{#7698C4}%k%f " + +[[ -z $precmd_functions ]] && precmd_functions=() precmd_functions=($precmd_functions update_prompt) diff --git a/.local/bin/brainmassage b/.local/bin/brainmassage new file mode 100755 index 0000000..57796f3 --- /dev/null +++ b/.local/bin/brainmassage @@ -0,0 +1,54 @@ +#!/bin/sh + +parse_query() { + sed "s/ /+/g" +} + +unquery() { + sed "s/+/ /g" +} + +artist() { + artist_name=$(printf "" | dmenu -p "Search Artist" | parse_query) + json=$(wget -qO- "https://musicbrainz.org/ws/2/release-group/?fmt=json&artist=$(search_artist $artist_name)&type=album|ep" 2>/dev/null) + album_name=$(echo "$json" | jq '."release-groups"[] | .title' | sed 's/"//g' | dmenu -p "Select Album") + album_id=$(echo "$json" | jq ".\"release-groups\"[] | select(.title==\"$album_name\") .id" | sed "s/\"//g") + release_id=$(wget -qO- "https://musicbrainz.org/ws/2/release/?fmt=json&release-group=${album_id}" 2>/dev/null | jq '.releases[0] .id' | sed 's/"//g') + + recording_names=$(wget -qO- "https://musicbrainz.org/ws/2/recording/?fmt=json&release=${release_id}" 2>/dev/null | jq '.recordings[] | .title' | sed 's/"//g') + + recordings_query=$(echo "$recording_names" | parse_query) + + mkdir "$album_name" + cd "$album_name" + + echo "Download $album_name" + + for name in $recordings_query; do + echo "Download $(echo ${name} | unquery)" + download "${artist_name}" "${album_name}" "${name}" & + done + + wait +} + +download() { + artist_name="$1" + album_name="$2" + name="$3" + + query=$(echo "${artist_name} ${album_name} ${name}" | parse_query) + video_url=$(wget -qO- "https://www.youtube.com/results?search_query=${query}" 2>/dev/null | grep -E -o '"/watch\?v=[^"]*"' | sed -e 's/"//g' | sed -e "s/\/watch/https:\/\/www.youtube.com\/watch/g" | head -n1) + youtube-dl -q --extract-audio --audio-format mp3 "${video_url}" -o "${name}.%(ext)s" + mp3info -a "$(echo "$artist_name" | unquery)" -l "$album_name" -t "$(echo $name | sed 's/+/ /g')" "${name}.mp3" + mv "${name}.mp3" "$(echo $name | sed 's/+/ /g').mp3" +} + +search_artist() { + json=$(wget -qO- "https://musicbrainz.org/ws/2/artist/?fmt=json&limit=100&query=$1" 2>/dev/null) + + artist_name=$(echo "$json" | jq ".artists[] | .name" | sed "s/\"//g" | dmenu -p "Select Artist") + echo "$json" | jq ".artists[] | select(.name==\"$artist_name\") .id" | sed "s/\"//g" +} + +artist diff --git a/.local/bin/clock b/.local/bin/clock new file mode 100755 index 0000000..a4eb334 --- /dev/null +++ b/.local/bin/clock @@ -0,0 +1,11 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) notify-send "This Month" "$(cal --color=always | sed "s/..7m/<b><span color=\"red\">/;s/..27m/<\/span><\/b>/")" && notify-send "Appointments" "$(calcurse -d3)" ;; + 2) setsid -f "$TERMINAL" -e calcurse ;; + 3) notify-send "📅 Time/date module" "\- Left click to show upcoming appointments for the next three days via \`calcurse -d3\` and show the month via \`cal\` +- Middle click opens calcurse if installed" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +date "+%H:%M %d %b %Y" diff --git a/.local/bin/cmus-refresh b/.local/bin/cmus-refresh new file mode 100755 index 0000000..044561b --- /dev/null +++ b/.local/bin/cmus-refresh @@ -0,0 +1,5 @@ +#!/bin/sh + +cmus-remote -C clear +cmus-remote -C "add /mnt/data/Musik/Musik/" +cmus-remote -C "update-cache -f" diff --git a/.local/bin/cmus_edit_tags b/.local/bin/cmus_edit_tags new file mode 100755 index 0000000..955997b --- /dev/null +++ b/.local/bin/cmus_edit_tags @@ -0,0 +1,9 @@ +#!/bin/bash +file="$(cmus-remote -C 'echo {}')" + +if [ -f "$file" ] +then + st -e mp3info -i "$file" & +else + echo "Oops, couldn't find selected track" >&2 +fi diff --git a/.local/bin/disk b/.local/bin/disk new file mode 100755 index 0000000..e947509 --- /dev/null +++ b/.local/bin/disk @@ -0,0 +1,23 @@ +#!/bin/sh + +# Status bar module for disk space +# $1 should be drive mountpoint, otherwise assumed /. + +location=${1:-/} + +[ -d "$location" ] || exit + +case $BLOCK_BUTTON in + 1) notify-send "💽 Disk space" "$(df -h --output=target,used,size)" ;; + 3) notify-send "💽 Disk module" "\- Shows used hard drive space. +- Click to show all disk info." ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +case "$location" in + "/home"* ) icon="🏠" ;; + "/mnt"* ) icon="💾" ;; + *) icon="🖥";; +esac + +printf "%s: %s\n" "$icon" "$(df -h "$location" | awk ' /[0-9]/ {print $3 "/" $2}')" diff --git a/.local/bin/dmount b/.local/bin/dmount new file mode 100755 index 0000000..495410f --- /dev/null +++ b/.local/bin/dmount @@ -0,0 +1,14 @@ +#/bin/sh + +disks=$(lsblk -o PATH,SIZE,TYPE,LABEL | grep "part" | grep -v "part $" | awk '{print $1 ":", $4, "[" $2 "]"}') + +chosen=$(echo $disks | dmenu -p "Mount disk:") + +[[ "$chosen" = "" ]] && exit 1 + +mountpoint=$(echo $chosen | awk '{print "/mnt/" $2}' | dmenu -p "Mountpoint:") + +[[ "$mountpoint" = "" ]] && exit 1 + +mkdir -p $mountpoint +sudo mount $(echo $chosen | awk '{print $1}' | sed 's/://') $mountpoint diff --git a/.local/bin/es b/.local/bin/es new file mode 100755 index 0000000..e162559 --- /dev/null +++ b/.local/bin/es @@ -0,0 +1,3 @@ +#!/bin/sh + +nvim $HOME/.local/bin/$(ls $HOME/.local/bin | fzf) diff --git a/.local/bin/fzfimg b/.local/bin/fzfimg new file mode 100755 index 0000000..b33ffb0 --- /dev/null +++ b/.local/bin/fzfimg @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# This is just an example how ueberzug can be used with fzf. +# Copyright (C) 2019 Nico Bäurer + +# 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 <https://www.gnu.org/licenses/>. +readonly BASH_BINARY="$(which bash)" +readonly REDRAW_COMMAND="toggle-preview+toggle-preview" +readonly REDRAW_KEY="µ" +declare -r -x DEFAULT_PREVIEW_POSITION="right" +declare -r -x UEBERZUG_FIFO="$(mktemp --dry-run --suffix "fzf-$$-ueberzug")" +declare -r -x PREVIEW_ID="preview" + + +function is_option_key [[ "${@}" =~ ^(\-.*|\+.*) ]] +function is_key_value [[ "${@}" == *=* ]] + + +function map_options { + local -n options="${1}" + local -n options_map="${2}" + + for ((i=0; i < ${#options[@]}; i++)); do + local key="${options[$i]}" next_key="${options[$((i + 1))]:---}" + local value=true + is_option_key "${key}" || \ + continue + if is_key_value "${key}"; then + <<<"${key}" \ + IFS='=' read key value + elif ! is_option_key "${next_key}"; then + value="${next_key}" + fi + options_map["${key}"]="${value}" + done +} + + +function parse_options { + declare -g -a script_options=("${@}") + declare -g -A mapped_options + map_options script_options mapped_options + declare -g -r -x PREVIEW_POSITION="${mapped_options[--preview-window]%%:[^:]*}" +} + + +function start_ueberzug { + mkfifo "${UEBERZUG_FIFO}" + <"${UEBERZUG_FIFO}" \ + ueberzug layer --parser bash --silent & + # prevent EOF + 3>"${UEBERZUG_FIFO}" \ + exec +} + + +function finalise { + 3>&- \ + exec + &>/dev/null \ + rm "${UEBERZUG_FIFO}" + &>/dev/null \ + kill $(jobs -p) +} + + +function calculate_position { + # TODO costs: creating processes > reading files + # so.. maybe we should store the terminal size in a temporary file + # on receiving SIGWINCH + # (in this case we will also need to use perl or something else + # as bash won't execute traps if a command is running) + < <(</dev/tty stty size) \ + read TERMINAL_LINES TERMINAL_COLUMNS + + case "${PREVIEW_POSITION:-${DEFAULT_PREVIEW_POSITION}}" in + left|up|top) + X=1 + Y=1 + ;; + right) + X=$((TERMINAL_COLUMNS - COLUMNS - 2)) + Y=1 + ;; + down|bottom) + X=1 + Y=$((TERMINAL_LINES - LINES - 1)) + ;; + esac +} + + +function draw_preview { + calculate_position + + >"${UEBERZUG_FIFO}" declare -A -p cmd=( \ + [action]=add [identifier]="${PREVIEW_ID}" \ + [x]="${X}" [y]="${Y}" \ + [width]="${COLUMNS}" [height]="${LINES}" \ + [scaler]=forced_cover [scaling_position_x]=0.5 [scaling_position_y]=0.5 \ + [path]="${@}") + # add [synchronously_draw]=True if you want to see each change +} + + +function print_on_winch { + # print "$@" to stdin on receiving SIGWINCH + # use exec as we will only kill direct childs on exiting, + # also the additional bash process isn't needed + </dev/tty \ + exec perl -e ' + require "sys/ioctl.ph"; + while (1) { + local $SIG{WINCH} = sub { + ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV; + }; + sleep; + }' \ + "${@}" & +} + + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + trap finalise EXIT + parse_options "${@}" + # print the redraw key twice as there's a run condition we can't circumvent + # (we can't know the time fzf finished redrawing it's layout) + print_on_winch "${REDRAW_KEY}${REDRAW_KEY}" + start_ueberzug + + export -f draw_preview calculate_position + SHELL="${BASH_BINARY}" \ + fzf --preview "draw_preview {}" \ + --preview-window "${DEFAULT_PREVIEW_POSITION}" \ + --bind "${REDRAW_KEY}:${REDRAW_COMMAND}" \ + "${@}" +fi diff --git a/.local/bin/internet b/.local/bin/internet new file mode 100755 index 0000000..603714c --- /dev/null +++ b/.local/bin/internet @@ -0,0 +1,22 @@ +#!/bin/sh + +# Show wifi 📶 and percent strength or 📡 if none. +# Show 🌐 if connected to ethernet or ❎ if none. + +case $BLOCK_BUTTON in + 1) "$TERMINAL" -e nmtui; pkill -RTMIN+4 dwmblocks ;; + 3) notify-send "🌍 Internet module" "\- Click to connect +📡: no wifi connection +📶: wifi connection with quality +❎: no ethernet +🌍: ethernet working +" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +case "$(cat /sys/class/net/w*/operstate 2>/dev/null)" in + down) wifiicon="📡 " ;; + up) wifiicon="$(awk '/^\s*w/ { print "📶", int($3 * 100 / 70) "% " }' /proc/net/wireless)" ;; +esac + +printf "%s%s\n" "$wifiicon" "$(sed "s/down/❎/;s/up/⬌/" /sys/class/net/e*/operstate 2>/dev/null)" diff --git a/.local/bin/kbselect b/.local/bin/kbselect new file mode 100755 index 0000000..d7313c6 --- /dev/null +++ b/.local/bin/kbselect @@ -0,0 +1,16 @@ +#!/bin/sh +# works on any init system +# requirements: dmenu, xorg-setxkbmap, xkblayout-state (https://github.com/nonpop/xkblayout-state) +kb="$(xkblayout-state print "%s")" || exit 1 + +case $BLOCK_BUTTON in + 1) kb_choice="$(awk '/! layout/{flag=1; next} /! variant/{flag=0} flag {print $2, "- " $1}' /usr/share/X11/xkb/rules/base.lst | dmenu -l 15)" + kb="$(echo "$kb_choice" | awk '{print $3}')" + setxkbmap "$kb" + pkill -RTMIN+30 "${STATUSBAR:-dwmblocks}";; + 3) notify-send "⌨ Keyboard/language module" "$(xkblayout-state print "\- Current layout: %s (%n)") +- Left click to change keyboard.";; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +echo "$kb" diff --git a/.local/bin/libview b/.local/bin/libview new file mode 100755 index 0000000..5f96d73 --- /dev/null +++ b/.local/bin/libview @@ -0,0 +1,17 @@ +#!/bin/sh + +cd /usr/include + +file=$(find . -name "*.h" | sed -e 's/^.\///g' | fzf) + +#echo $file + +fname=$(grep -E '^extern .*;$' "/usr/include/$file" | awk '{ print $3 }' | fzf) + +page=$(whatis "$fname" | sed -E 's/([A-Za-z]*) \(([^)]*)\).*/\2 \1/g') + +selpage="$page" + +[ "$(echo "$page" | wc -l)" -ne 1 ] && selpage=$(echo "$page" | fzf) + +man $selpage diff --git a/.local/bin/manuals b/.local/bin/manuals new file mode 100755 index 0000000..8b57b2b --- /dev/null +++ b/.local/bin/manuals @@ -0,0 +1,2 @@ +#!/bin/sh +st man $(man -k - | dmenu | awk '{print $1}') diff --git a/.local/bin/map b/.local/bin/map new file mode 100755 index 0000000..1b19380 --- /dev/null +++ b/.local/bin/map @@ -0,0 +1,8 @@ +#!/bin/sh + +keylayout=$(localectl list-x11-keymap-layouts | dmenu -p "Select keyboard layouts") +keymodel=$(localectl list-x11-keymap-variants | dmenu -p "Select keyboard model") + +setxkbmap $keylayout $keymodel + +notify-send "Set Keyboardlayout" "Layout: $keylayout\nModel: $keymodel" diff --git a/.local/bin/memory b/.local/bin/memory new file mode 100755 index 0000000..01d3daf --- /dev/null +++ b/.local/bin/memory @@ -0,0 +1,12 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) notify-send "🧠 Memory hogs" "$(ps axch -o cmd:15,%mem --sort=-%mem | head)" ;; + 2) setsid -f "$TERMINAL" -e htop ;; + 3) notify-send "🧠 Memory module" "\- Shows Memory Used/Total. +- Click to show memory hogs. +- Middle click to open htop." ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +free --mebi | sed -n '2{p;q}' | awk '{printf ("🧠%2.2fGiB/%2.2fGiB\n", ( $3 / 1024), ($2 / 1024))}' diff --git a/.local/bin/messages b/.local/bin/messages new file mode 100755 index 0000000..a4d2427 --- /dev/null +++ b/.local/bin/messages @@ -0,0 +1,3 @@ +#!/bin/sh + +$BROWSER https://messages.google.com/web diff --git a/.local/bin/mkbackup b/.local/bin/mkbackup new file mode 100755 index 0000000..e0d5e69 --- /dev/null +++ b/.local/bin/mkbackup @@ -0,0 +1 @@ +duplicity --progress $HOME --exclude $HOME/.cache file:///mnt/backups diff --git a/.local/bin/ncureses-extended b/.local/bin/ncureses-extended Binary files differnew file mode 100755 index 0000000..8ebac35 --- /dev/null +++ b/.local/bin/ncureses-extended diff --git a/.local/bin/nettraf b/.local/bin/nettraf new file mode 100755 index 0000000..71e6fe4 --- /dev/null +++ b/.local/bin/nettraf @@ -0,0 +1,28 @@ +#!/bin/sh + +# Module showing network traffic. Shows how much data has been received (RX) or +# transmitted (TX) since the previous time this script ran. So if run every +# second, gives network traffic per second. + +case $BLOCK_BUTTON in + 3) notify-send "🌐 Network traffic module" "🔻: Traffic received +🔺: Traffic transmitted" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +update() { + sum=0 + for arg; do + read -r i < "$arg" + sum=$(( sum + i )) + done + cache=${XDG_CACHE_HOME:-$HOME/.cache}/${1##*/} + [ -f "$cache" ] && read -r old < "$cache" || old=0 + printf %d\\n "$sum" > "$cache" + printf %d\\n $(( sum - old )) +} + +rx=$(update /sys/class/net/[ew]*/statistics/rx_bytes) +tx=$(update /sys/class/net/[ew]*/statistics/tx_bytes) + +printf "🔻%4sB 🔺%4sB\\n" $(numfmt --to=iec $rx) $(numfmt --to=iec $tx) diff --git a/.local/bin/news b/.local/bin/news new file mode 100755 index 0000000..fe701db --- /dev/null +++ b/.local/bin/news @@ -0,0 +1,17 @@ +#!/bin/sh + +# Displays number of unread news items and an loading icon if updating. +# When clicked, brings up `newsboat`. + +case $BLOCK_BUTTON in + 1) setsid "$TERMINAL" -e newsboat ;; + 2) setsid -f newsup >/dev/null exit ;; + 3) notify-send "📰 News module" "\- Shows unread news items +- Shows 🔃 if updating with \`newsup\` +- Left click opens newsboat +- Middle click syncs RSS feeds +<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time." ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + + cat /tmp/newsupdate 2>/dev/null || echo "$(newsboat -x print-unread | awk '{ if($1>0) print "📰" $1}')$(cat "${XDG_CONFIG_HOME:-$HOME/.config}"/newsboat/.update 2>/dev/null)" diff --git a/.local/bin/outset b/.local/bin/outset new file mode 100755 index 0000000..397cbd5 --- /dev/null +++ b/.local/bin/outset @@ -0,0 +1,3 @@ +#!/bin/sh + +setsid swaymsg "output VGA-1 mode --custom 1600x900" diff --git a/.local/bin/pacpackages b/.local/bin/pacpackages new file mode 100755 index 0000000..8c86e6d --- /dev/null +++ b/.local/bin/pacpackages @@ -0,0 +1,31 @@ +#!/bin/sh + +# Displays number of upgradeable packages. +# For this to work, have a `pacman -Sy` command run in the background as a +# cronjob every so often as root. This script will then read those packages. +# When clicked, it will run an upgrade via pacman. +# +# Add the following text as a file in /usr/share/libalpm/hooks/statusbar.hook: +# +# [Trigger] +# Operation = Upgrade +# Type = Package +# Target = * +# +# [Action] +# Description = Updating statusbar... +# When = PostTransaction +# Exec = /usr/bin/pkill -RTMIN+8 dwmblocks # Or i3blocks if using i3. + +case $BLOCK_BUTTON in + 1) setsid -f "$TERMINAL" -e popupgrade ;; + 2) notify-send "$(/usr/bin/pacman -Qu)" ;; + 3) notify-send "🎁 Upgrade module" "📦: number of upgradable packages +- Left click to upgrade packages +- Middle click to show upgradable packages" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +n=$(pacman -Qu | grep -Fcv "[ignored]") + +[ "$n" = "0" ] && echo "✓"; diff --git a/.local/bin/playjazz b/.local/bin/playjazz new file mode 100755 index 0000000..d4f327c --- /dev/null +++ b/.local/bin/playjazz @@ -0,0 +1 @@ +wget -qO- "http://stream.srg-ssr.ch/m/rsj/mp3_128" | mpv - diff --git a/.local/bin/podfetch b/.local/bin/podfetch new file mode 100755 index 0000000..ce8cf6c --- /dev/null +++ b/.local/bin/podfetch @@ -0,0 +1,21 @@ +#! /bin/sh + +feeds='Brainpain|https://w8gez1.podcaster.de/Brainpain.rss' + +feed=$(printf "$feeds" | column -t -s"|" | dmenu | awk '{print $NF}') + +echo "Fetching feed: $feed" + +download=$(wget -qO- $feed | grep "<title>\|<enclosure " | tail -n+2 | awk '{$1=$1};1' | tr '\n' ' ' | sed -e "s/\/> /\/>\n/g" | sed -e "s/<title>//g" | sed -e "s/<\/title>//g" | sed -e "s/ <enclosure type.*url=/´/g" | sed -e "s/\/>$//g" | column -t -s"´" | dmenu) + +link=$(echo $download | awk '{print $NF}' | sed -e "s/\"//g") +name=$(echo $download | sed -e "s/ \".*\"//g") +filename=$(echo $name | tr ' ' '_')$(echo $link | sed -e "s/.*\././g") + +echo "Link: $link" +echo "Filename: $filename" + +notify-send "Downloading Podcast" "$name" + +wget -O "/mnt/data/Podcasts/$filename" "$link" && notify-send "Finished Downloading Podcast" "$name" || notify-send "Download Failed" "$name" + diff --git a/.local/bin/podplay b/.local/bin/podplay new file mode 100755 index 0000000..3619286 --- /dev/null +++ b/.local/bin/podplay @@ -0,0 +1,2 @@ +#!/bin/sh +mpv /mnt/data/Podcasts/$(ls /mnt/data/Podcasts | dmenu -p "Play: ") diff --git a/.local/bin/popupgrade b/.local/bin/popupgrade new file mode 100755 index 0000000..3a62727 --- /dev/null +++ b/.local/bin/popupgrade @@ -0,0 +1,9 @@ +#!/bin/sh + +printf "Beginning upgrade.\\n" + +yay -Syu +pkill -RTMIN+8 "${STATUSBAR:-dwmblocks}" + +printf "\\nUpgrade complete.\\nPress <Enter> to exit window.\\n\\n" +read -r diff --git a/.local/bin/powerdialog b/.local/bin/powerdialog new file mode 100755 index 0000000..642c45f --- /dev/null +++ b/.local/bin/powerdialog @@ -0,0 +1,15 @@ +#!/bin/sh + +options() { + selection=$(printf "Cancel\nSuspend\nPower Off\nLogout" | dmenu -p "Logout Dialog") + + [ "$selection" = "Suspend" ] && systemctl suspend && exit + [ "$selection" = "Power Off" ] && poweroff && exit + [ "$selection" = "Logout" ] && loginctl terminate-session self && exit +} + +case $BLOCK_BUTTON in + 1) options;; +esac + +printf "%s\n" "⏻" diff --git a/.local/bin/printcolors b/.local/bin/printcolors new file mode 100755 index 0000000..07f8663 --- /dev/null +++ b/.local/bin/printcolors @@ -0,0 +1,4 @@ +#!/bin/bash +for (( i = 0; i < 256; i++ )); + do printf "$(tput setaf $i)$i$(tput sgr0) "; +done diff --git a/.local/bin/screencast b/.local/bin/screencast new file mode 100755 index 0000000..0d64e7e --- /dev/null +++ b/.local/bin/screencast @@ -0,0 +1,11 @@ +#!/bin/sh + +name=$(date "+recording_%H-%M-%S_%d_%m_%y.mp4") + + +case "$(printf "Screen\nWindow\nRegion\nEnd Recording" | dmenu -p "Record")" in + Screen)ffcast -x "$(ffcast -x list | dmenu -p "Monitor" | gcol 2 | tr -d '#:')" rec "$name";; + Window)ffcast -w rec "$name";; + Region)ffcast -s rec "$name";; + End\ Recording)killall -s INT ffmpeg;; +esac diff --git a/.local/bin/search b/.local/bin/search new file mode 100755 index 0000000..38d8f27 --- /dev/null +++ b/.local/bin/search @@ -0,0 +1,2 @@ +# /bin/zsh +xdg-open $(find ~ -name $(printf "" | dmenu -p "Search Object: ") | dmenu -p "Open: ") diff --git a/.local/bin/showimg b/.local/bin/showimg new file mode 100755 index 0000000..38550a6 --- /dev/null +++ b/.local/bin/showimg @@ -0,0 +1,26 @@ +#!/bin/sh + +[ -z $1 ] && printf "no image given:\n$(echo $0 | tr '/' ' ' | awk '{print $NF}') [Image]\n" && exit + +term_width=$(tput cols) +term_width=$(($term_width*8)) +term_height=$(tput lines) +term_height=$(($term_height*20)) + +size=$(identify $1 | awk '{print $3}' | tr 'x' ' ') +width=$(echo $size | awk '{print $1}') +height=$(echo $size | awk '{print $2}') +height=$(($height*$term_width/$width)) + +if [ $term_height -gt $height ] +then + $(printf "0;1;0;0;$term_width;$height;;;;;$1\n4;\n3;\n" | /usr/lib/w3m/w3mimgdisplay) & + read wt && exit +fi + +width=$(echo $size | awk '{print $1}') +height=$(echo $size | awk '{print $2}') +width=$(($width*$term_height/$height)) + +$(printf "1;0;0;0;$width;$term_height;;;;;$1\n4;\n3;\n" | /usr/lib/w3m/w3mimgdisplay) & +read wt && exit diff --git a/.local/bin/sprogs b/.local/bin/sprogs new file mode 100755 index 0000000..6785691 --- /dev/null +++ b/.local/bin/sprogs @@ -0,0 +1,5 @@ +#!/bin/sh + +kdeconnect-indicator & +syncthing --no-browser & +nextcloud & diff --git a/.local/bin/startsession b/.local/bin/startsession new file mode 100755 index 0000000..443aca5 --- /dev/null +++ b/.local/bin/startsession @@ -0,0 +1,6 @@ +#!/bin/bash + +export XINITRC="$XDG_CONFIG_HOME/X11/xinitrc" + +# Start Session +[ "$(fgconsole 2>/dev/null)" = "1" ] && exec startx ~/.config/X11/xinitrc -- vt1 &> /dev/null diff --git a/.local/bin/storeless b/.local/bin/storeless new file mode 100755 index 0000000..a7114d5 --- /dev/null +++ b/.local/bin/storeless @@ -0,0 +1,46 @@ +#!/bin/sh + +store() { + selection=$(yay -Ss | sed -z 's/\n\s\+/ - /g' | sed -E 's/^[^\/]*\///g' | sed -e 's/([0-9.]* .iB /(/g' | sed -e 's/\[base-devel\]//g' | sed -e 's/ \+/ /g' | dmenu) + + name=$(echo $selection | awk '{ print $1 }') + + if [ -z "$name" ]; then + exit + fi + + if [ -z "$(yay -Qs $name)" ]; then + selection=$(printf 'Install\nInfo\nBack' | dmenu -p "Package: $name ") + else + selection=$(printf 'Remove\nInfo\nBack' | dmenu -p "Package: $name ") + fi + + echo $name + + case "$selection" in + Install)window "yay -S $name" && notify-send "Storeless" "$name Successfully Installed";; + Remove)window "yay -Rns $name" && notify-send "Storeless" "$name Successfully Removed ";; + Info)notify-send -t 0 "$name" "$(yay -Si $name)";; + Back)store;; + esac +} + +primaryscreenwidth() { + xrandr | grep primary | awk '{ print $4 }' | grep -o '^[0-9]*' +} + +window() { + st -g 108x24+10+40 -c floating $1 +} + +update() { + window yay + notify-send "Storeless" "System Up To Date" +} + +case "$(printf "Store\nUpdate" | dmenu)" in + Store)store;; + Update)update;; +esac + + diff --git a/.local/bin/torrent b/.local/bin/torrent new file mode 100755 index 0000000..9e1da3a --- /dev/null +++ b/.local/bin/torrent @@ -0,0 +1,33 @@ +#!/bin/sh + +transmission-remote -l | grep % | + sed " # This first sed command is to ensure a desirable order with sort + s/.*Stopped.*/A/g; + s/.*Seeding.*/Z/g; + s/.*100%.*/N/g; + s/.*Idle.*/B/g; + s/.*Uploading.*/L/g; + s/.*%.*/M/g" | + sort -h | uniq -c | sed " # Now we replace the standin letters with icons. + s/A/🛑/g; + s/B/🕰/g; + s/L/🔼/g; + s/M/🔽/g; + s/N/✅/g; + s/Z/🌱/g" | awk '{print $2 $1}' | paste -sd ' ' + +case $BLOCK_BUTTON in + 1) setsid -f "$TERMINAL" -e tremc ;; + 2) td-toggle ;; + 3) notify-send "🌱 Torrent module" "\- Left click to open tremc. +- Middle click to toggle transmission. +- Shift click to edit script. +Module shows number of torrents: +🛑: paused +🕰: idle (seeds needed) +🔼: uploading (unfinished) +🔽: downloading +✅: done +🌱: done and seeding" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac diff --git a/.local/bin/tvimb b/.local/bin/tvimb new file mode 100755 index 0000000..7aa76b7 --- /dev/null +++ b/.local/bin/tvimb @@ -0,0 +1,2 @@ +#!/bin/sh +GDK_BACKEND=x11 tabbed -c vimb -e diff --git a/.local/bin/updates b/.local/bin/updates new file mode 100755 index 0000000..b3acb48 --- /dev/null +++ b/.local/bin/updates @@ -0,0 +1,15 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) setsid storeless;; + 3) notify-send "📦 Upates" "$(yay -Qu)";; +esac + +packages=$(yay -Sy >/dev/null && yay -Qu) + +if [ -n "$packages" ] +then + echo $(echo "$packages" | wc -l) ⏬ +else + echo 🔁 +fi diff --git a/.local/bin/volume b/.local/bin/volume new file mode 100755 index 0000000..3fac23e --- /dev/null +++ b/.local/bin/volume @@ -0,0 +1,28 @@ +#!/bin/sh + +# Prints the current volume or 🔇 if muted. + +case $BLOCK_BUTTON in + 1) setsid -f "$TERMINAL" -e pulsemixer ;; + 2) pamixer -t ; pkill -RTMIN+10 dwmblocks;; + 4) pamixer --allow-boost -i 1 ;; + 5) pamixer --allow-boost -d 1 ;; + 3) notify-send "📢 Volume module" "\- Shows volume 🔊, 🔇 if muted. +- Middle click to mute. +- Scroll to change." ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +[ $(pamixer --get-mute) = true ] && echo 🔇 && exit + +vol="$(pamixer --sink 0 --get-volume)" + +if [ "$vol" -gt "70" ]; then + icon="🔊" +elif [ "$vol" -lt "30" ]; then + icon="🔈" +else + icon="🔉" +fi + +echo "$vol% $icon" diff --git a/.local/bin/weather b/.local/bin/weather new file mode 100755 index 0000000..e04bac0 --- /dev/null +++ b/.local/bin/weather @@ -0,0 +1,35 @@ +#!/bin/sh + +# Displays todays precipication chance (☔) and daily low (🥶) and high (🌞). +# Usually intended for the statusbar. + +# If we have internet, get a weather report from wttr.in and store it locally. +# You could set up a shell alias to view the full file in a pager in the +# terminal if desired. This function will only be run once a day when needed. +weatherreport="${XDG_DATA_HOME:-$HOME/.local/share}/weatherreport" +getforecast() { curl -sf "wttr.in/$LOCATION" > "$weatherreport" || exit 1 ;} + +# Some very particular and terse stream manipulation. We get the maximum +# precipication chance and the daily high and low from the downloaded file and +# display them with coresponding emojis. +showweather() { printf "%s" "$(sed '16q;d' "$weatherreport" | + grep -wo "[0-9]*%" | sort -rn | sed "s/^/☔/g;1q" | tr -d '\n')" +sed '13q;d' "$weatherreport" | grep -o "m\\([-+]\\)*[0-9]\\+" | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print " 🥶" $1 "°","🌞" $2 "°"}' ;} + +case $BLOCK_BUTTON in + 1) setsid -f "$TERMINAL" -e less -Srf "$weatherreport" ;; + 2) getforecast && showweather ;; + 3) notify-send "🌈 Weather module" "\- Left click for full forecast. +- Middle click to update forecast. +☔: Chance of rain/snow +🥶: Daily low +🌞: Daily high" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +# The test if our forcecast is updated to the day. If it isn't download a new +# weather report from wttr.in with the above function. +[ "$(stat -c %y "$weatherreport" 2>/dev/null | cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ] || + getforecast + +showweather diff --git a/.local/bin/xstart_dwm b/.local/bin/xstart_dwm new file mode 100755 index 0000000..bf1ec4a --- /dev/null +++ b/.local/bin/xstart_dwm @@ -0,0 +1,12 @@ +#!/bin/sh + +while true; do + # Log stderror to a file + dunst & + st cmus & + dwmblocks 2>&1 > /dev/null & + dwm 2> ~/.cache/dwm.log + killall dwmblocks + # No error logging + #dwm >/dev/null 2>&1 +done diff --git a/.local/share/nvim/site/autoload/brackify.vim b/.local/share/nvim/site/autoload/brackify.vim new file mode 100644 index 0000000..ca2a834 --- /dev/null +++ b/.local/share/nvim/site/autoload/brackify.vim @@ -0,0 +1,39 @@ +function! CharAtIdx(str, idx) abort + return strcharpart(a:str, a:idx, 1) +endfunction + +function! CursorCharIdx() abort + let cursor_byte_idx = col('.') + if cursor_byte_idx == 1 + return 0 + endif + + let pre_cursor_text = getline('.')[:col('.')-2] + return strchars(pre_cursor_text) +endfunction + +function brackify#putbracket(bracket) + let cur_char_idx = CursorCharIdx() + let cur_char = CharAtIdx(getline('.'), cur_char_idx + 1) + + if cur_char != a:bracket + let line = getline('.')[:col('.') - 1] . a:bracket . getline('.')[col('.'):] + call setline(line("."), line) + endif +endfunction + +function brackify#putquotes(quote) + let cur_char_idx = CursorCharIdx() + let cur_char = CharAtIdx(getline('.'), cur_char_idx + 1) + + if cur_char != a:quote + let line = getline('.')[:col('.') - 1] . a:quote . a:quote . getline('.')[col('.'):] + call setline(line("."), line) + + if col('.') > 1 + call cursor(getpos('.')[1], col('.') + 1) + endif + else + call cursor(getpos('.')[1], col('.') + 1) + endif +endfunction diff --git a/.local/share/nvim/site/autoload/plug.vim b/.local/share/nvim/site/autoload/plug.vim new file mode 100644 index 0000000..46416b8 --- /dev/null +++ b/.local/share/nvim/site/autoload/plug.vim @@ -0,0 +1,2804 @@ +" vim-plug: Vim plugin manager +" ============================ +" +" Download plug.vim and put it in ~/.vim/autoload +" +" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" +" Edit your .vimrc +" +" call plug#begin('~/.vim/plugged') +" +" " Make sure you use single quotes +" +" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align +" Plug 'junegunn/vim-easy-align' +" +" " Any valid git URL is allowed +" Plug 'https://github.com/junegunn/vim-github-dashboard.git' +" +" " Multiple Plug commands can be written in a single line using | separators +" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' +" +" " On-demand loading +" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } +" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } +" +" " Using a non-default branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } +" +" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) +" Plug 'fatih/vim-go', { 'tag': '*' } +" +" " Plugin options +" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } +" +" " Plugin outside ~/.vim/plugged with post-update hook +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +" +" " Unmanaged plugin (manually installed and updated) +" Plug '~/my-prototype-plugin' +" +" " Initialize plugin system +" call plug#end() +" +" Then reload .vimrc and :PlugInstall to install plugins. +" +" Plug options: +" +"| Option | Description | +"| ----------------------- | ------------------------------------------------ | +"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | +"| `rtp` | Subdirectory that contains Vim plugin | +"| `dir` | Custom directory for the plugin | +"| `as` | Use different name for the plugin | +"| `do` | Post-update hook (string or funcref) | +"| `on` | On-demand loading: Commands or `<Plug>`-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand('<sfile>:p')) + set shellslash +else + let s:me = resolve(expand('<sfile>:p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! s:is_powershell(shell) + return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$' +endfunction + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif has('nvim') + let home = stdpath('data') . '/plugged' + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#(<args>) + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + if has('win32') + \ && &shellslash + \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell)) + return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') + endif + if !has('nvim') + \ && (has('win32') || has('win32unix')) + \ && !has('multi_byte') + return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>]) + command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>]) + command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0) + command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif + command! -nargs=0 -bar PlugStatus call s:status() + command! -nargs=0 -bar PlugDiff call s:diff() + command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>) +endfunction + +function! s:to_a(v) + return type(a:v) == s:TYPE.list ? a:v : [a:v] +endfunction + +function! s:to_s(v) + return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n" +endfunction + +function! s:glob(from, pattern) + return s:lines(globpath(a:from, a:pattern)) +endfunction + +function! s:source(from, ...) + let found = 0 + for pattern in a:000 + for vim in s:glob(a:from, pattern) + execute 'source' s:esc(vim) + let found = 1 + endfor + endfor + return found +endfunction + +function! s:assoc(dict, key, val) + let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) +endfunction + +function! s:ask(message, ...) + call inputsave() + echohl WarningMsg + let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) ')) + echohl None + call inputrestore() + echo "\r" + return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0 +endfunction + +function! s:ask_no_interrupt(...) + try + return call('s:ask', a:000) + catch + return 0 + endtry +endfunction + +function! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('plug#end() called without calling plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') + let s:loaded[name] = 1 + continue + endif + + if has_key(plug, 'on') + let s:triggers[name] = { 'map': [], 'cmd': [] } + for cmd in s:to_a(plug.on) + if cmd =~? '^<Plug>.\+' + if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) + call s:assoc(lod.map, cmd, name) + endif + call add(s:triggers[name].map, cmd) + elseif cmd =~# '^[A-Z]' + let cmd = substitute(cmd, '!*$', '', '') + if exists(':'.cmd) != 2 + call s:assoc(lod.cmd, cmd, name) + endif + call add(s:triggers[name].cmd, cmd) + else + call s:err('Invalid `on` option: '.cmd. + \ '. Should start with an uppercase letter or `<Plug>`.') + endif + endfor + endif + + if has_key(plug, 'for') + let types = s:to_a(plug.for) + if !empty(types) + augroup filetypedetect + call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') + augroup END + endif + for type in types + call s:assoc(lod.ft, type, name) + endfor + endif + endfor + + for [cmd, names] in items(lod.cmd) + execute printf( + \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)', + \ cmd, string(cmd), string(names)) + endfor + + for [map, names] in items(lod.map) + for [mode, map_prefix, key_prefix] in + \ [['i', '<C-\><C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] + execute printf( + \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, %s, "%s")<CR>', + \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix) + endfor + endfor + + for [ft, names] in items(lod.ft) + augroup PlugLOD + execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)', + \ ft, string(ft), string(names)) + augroup END + endfor + + call s:reorg_rtp() + filetype plugin indent on + if has('vim_starting') + if has('syntax') && !exists('g:syntax_on') + syntax enable + end + else + call s:reload_plugins() + endif +endfunction + +function! s:loaded_names() + return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') +endfunction + +function! s:load_plugin(spec) + call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') +endfunction + +function! s:reload_plugins() + for name in s:loaded_names() + call s:load_plugin(g:plugs[name]) + endfor +endfunction + +function! s:trim(str) + return substitute(a:str, '[\/]\+$', '', '') +endfunction + +function! s:version_requirement(val, min) + for idx in range(0, len(a:min) - 1) + let v = get(a:val, idx, 0) + if v < a:min[idx] | return 0 + elseif v > a:min[idx] | return 1 + endif + endfor + return 1 +endfunction + +function! s:git_version_requirement(...) + if !exists('s:git_version') + let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)') + endif + return s:version_requirement(s:git_version, a:000) +endfunction + +function! s:progress_opt(base) + return a:base && !s:is_win && + \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' +endfunction + +function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) +endfunction + +if s:is_win + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction + + " Copied from fzf + function! s:wrap_cmds(cmds) + let cmds = [ + \ '@echo off', + \ 'setlocal enabledelayedexpansion'] + \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) + \ + ['endlocal'] + if has('iconv') + if !exists('s:codepage') + let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) + endif + return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) + endif + return map(cmds, 'v:val."\r"') + endfunction + + function! s:batchfile(cmd) + let batchfile = s:plug_tempname().'.bat' + call writefile(s:wrap_cmds(a:cmd), batchfile) + let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000) + endif +endfunction + +function! s:dobufread(names) + for name in a:names + let path = s:rtp(g:plugs[name]) + for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] + if len(finddir(dir, path)) + if exists('#BufRead') + doautocmd BufRead + endif + return + endif + endfor + endfor +endfunction + +function! plug#load(...) + if a:0 == 0 + return s:err('Argument missing: plugin name(s) required') + endif + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 + let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') + if !empty(unknowns) + let s = len(unknowns) > 1 ? 's' : '' + return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) + end + let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') + if !empty(unloaded) + for name in unloaded + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + call s:dobufread(unloaded) + return 1 + end + return 0 +endfunction + +function! s:remove_triggers(name) + if !has_key(s:triggers, a:name) + return + endif + for cmd in s:triggers[a:name].cmd + execute 'silent! delc' cmd + endfor + for map in s:triggers[a:name].map + execute 'silent! unmap' map + execute 'silent! iunmap' map + endfor + call remove(s:triggers, a:name) +endfunction + +function! s:lod(names, types, ...) + for name in a:names + call s:remove_triggers(name) + let s:loaded[name] = 1 + endfor + call s:reorg_rtp() + + for name in a:names + let rtp = s:rtp(g:plugs[name]) + for dir in a:types + call s:source(rtp, dir.'/**/*.vim') + endfor + if a:0 + if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) + execute 'runtime' a:1 + endif + call s:source(rtp, a:2) + endif + call s:doautocmd('User', name) + endfor +endfunction + +function! s:lod_ft(pat, names) + let syn = 'syntax/'.a:pat.'.vim' + call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) + execute 'autocmd! PlugLOD FileType' a:pat + call s:doautocmd('filetypeplugin', 'FileType') + call s:doautocmd('filetypeindent', 'FileType') +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) +endfunction + +function! s:lod_map(map, names, with_prefix, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + + if a:with_prefix + let prefix = v:count ? v:count : '' + let prefix .= '"'.v:register.a:prefix + if mode(1) == 'no' + if v:operator == 'c' + let prefix = "\<esc>" . prefix + endif + let prefix .= v:operator + endif + call feedkeys(prefix, 'n') + endif + call feedkeys(substitute(a:map, '^<Plug>', "\<Plug>", '') . extra) +endfunction + +function! plug#(repo, ...) + if a:0 > 1 + return s:err('Invalid number of arguments (1..2)') + endif + + try + let repo = s:trim(a:repo) + let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec + let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(repo . ' ' . v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' + if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif + let opts.tag = a:arg + elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-\{1}\ / + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap <silent> <buffer> R :call <SID>retry()<cr> + nnoremap <silent> <buffer> D :PlugDiff<cr> + nnoremap <silent> <buffer> S :PlugStatus<cr> + nnoremap <silent> <buffer> U :call <SID>status_update()<cr> + xnoremap <silent> <buffer> U :call <SID>status_update()<cr> + nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr> + nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr> +endfunction + +function! s:prepare(...) + if empty(s:plug_getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap <silent> <buffer> q :call <SID>close_pane()<cr> + if a:0 == 0 + call s:finish_bindings() + endif + let b:plug_preview = -1 + let s:plug_tab = tabpagenr() + let s:plug_buf = winbufnr(0) + call s:assign_name() + + for k in ['<cr>', 'L', 'o', 'X', 'd', 'dd'] + execute 'silent! unmap <buffer>' k + endfor + setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + else + bd + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win + set shell=sh + endif + if a:swap + if s:is_powershell(&shell) + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' + set shellredir=>%s\ 2>&1 + endif + endif + return prev +endfunction + +function! s:bang(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') + execute "normal! :execute g:_plug_bang\<cr>\<cr>" + finally + unlet g:_plug_bang + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + call s:load_plugin(spec) + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:git_revision(a:spec.dir) + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' + let output = s:system( + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + " Set remote name, overriding a possible user git config's clone.defaultRemoteName + let s:clone_opt = ['--origin', 'origin'] + if get(g:, 'plug_shallow', 1) + call extend(s:clone_opt, ['--depth', '1']) + if s:git_version_requirement(1, 7, 10) + call add(s:clone_opt, '--no-single-branch') + endif + endif + + if has('win32unix') || has('wsl') + call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = s:git_origin_branch(spec) + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return (a:event == 'stdout' || a:event == 'stderr') ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + + if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = a:cmd + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = s:plug_call('jobstart', argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) + if has_key(a:opts, 'dir') + let cmd = s:with_cd(cmd, a:opts.dir, 0) + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'err_mode': 'raw', + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + let max = line('$') + for i in range(4, max > 4 ? max : 4) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, max > 5 ? max : 5) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = empty(globpath(spec.dir, '.git', 1)) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, cmd, { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + let cmd = ['git', 'clone'] + if !has_tag + call extend(cmd, s:clone_opt) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt').join(' ') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') +endfunction + +function! s:shellesc_ps1(arg) + return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" +endfunction + +function! s:shellesc_sh(arg) + return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" +endfunction + +" Escape the shell argument based on the shell. +" Vim and Neovim's shellescape() are insufficient. +" 1. shellslash determines whether to use single/double quotes. +" Double-quote escaping is fragile for cmd.exe. +" 2. It does not work for powershell. +" 3. It does not work for *sh shells if the command is executed +" via cmd.exe (ie. cmd.exe /c sh -c command command_args) +" 4. It does not support batchfile syntax. +" +" Accepts an optional dictionary with the following keys: +" - shell: same as Vim/Neovim 'shell' option. +" If unset, fallback to 'cmd.exe' on Windows or 'sh'. +" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. +function! plug#shellescape(arg, ...) + if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' + return a:arg + endif + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\(\.exe\)\?$' + return s:shellesc_cmd(a:arg, script) + elseif s:is_powershell(shell) + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) +endfunction + +function! s:system(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + if type(a:cmd) == s:TYPE.list + " Neovim's system() supports list argument to bypass the shell + " but it cannot set the working directory for the command. + " Assume that the command does not rely on the shell. + if has('nvim') && a:0 == 0 + return system(a:cmd) + endif + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd + endif + if a:0 > 0 + let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) + endif + if s:is_win && type(a:cmd) != s:TYPE.list + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] + let remote = result[-1] + if empty(remote) + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let sha = s:git_revision(a:spec.dir) + if empty(sha) + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let current_branch = result[0] + " Check tag + let origin_branch = s:git_origin_branch(a:spec) + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif origin_branch !=# current_branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ current_branch, origin_branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ origin_branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap <silent> <buffer> d :set opfunc=<sid>delete_op<cr>g@ + nmap <silent> <buffer> dd d_ + xnoremap <silent> <buffer> d :<c-u>call <sid>delete_op(visualmode(), 1)<cr> + echo 'Delete the lines (d{motion}) to delete the corresponding directories' + endif + endif + 4 + setlocal nomodifiable +endfunction + +function! s:delete_op(type, ...) + call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0) +endfunction + +function! s:delete(range, force) + let [l1, l2] = a:range + let force = a:force + let err_count = 0 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + let err = s:rm_rf(line[2:]) + setlocal modifiable + if empty(err) + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + else + delete _ + call append(l1 - 1, s:format_message('x', line[1:], err)) + let l2 += len(s:lines(err)) + let err_count += 1 + endif + let msg = printf('Removed %d directories.', s:clean_count) + if err_count > 0 + let msg .= printf(' Failed to remove %d directories.', err_count) + endif + call setline(4, msg) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) + if v:shell_error + return s:err('Error upgrading vim-plug: '. out) + endif + + if readfile(s:me) ==# readfile(new) + echo 'vim-plug is already up-to-date' + return 0 + else + call rename(s:me, s:me . '.old') + call rename(new, s:me) + unlet g:loaded_plug + echo 'vim-plug has been upgraded' + return 1 + endif + finally + silent! call s:rm_rf(tmp) + endtry +endfunction + +function! s:upgrade_specs() + for spec in values(g:plugs) + let spec.frozen = get(spec, 'frozen', 0) + endfor +endfunction + +function! s:status() + call s:prepare() + call append(0, 'Checking plugins') + call append(1, '') + + let ecnt = 0 + let unloaded = 0 + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + let is_dir = isdirectory(spec.dir) + if has_key(spec, 'uri') + if is_dir + let [err, _] = s:git_validate(spec, 1) + let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if is_dir + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif + endif + let cnt += 1 + let ecnt += !valid + " `s:loaded` entry can be missing if PlugUpgraded + if is_dir && get(s:loaded, name, -1) == 0 + let unloaded = 1 + let msg .= ' (not loaded)' + endif + call s:progress_bar(2, repeat('=', cnt), total) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) + normal! 2G + redraw + endfor + call setline(1, 'Finished. '.ecnt.' error(s).') + normal! gg + setlocal nomodifiable + if unloaded + echo "Press 'L' on each line to load plugin, or 'U' to update" + nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> + xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> + end +endfunction + +function! s:extract_name(str, prefix, suffix) + return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') +endfunction + +function! s:status_load(lnum) + let line = getline(a:lnum) + let name = s:extract_name(line, '-', '(not loaded)') + if !empty(name) + call plug#load(name) + setlocal modifiable + call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) + setlocal nomodifiable + endif +endfunction + +function! s:status_update() range + let lines = getline(a:firstline, a:lastline) + let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') + if !empty(names) + echo + execute 'PlugUpdate' join(names) + endif +endfunction + +function! s:is_preview_window_open() + silent! wincmd P + if &previewwindow + wincmd p + return 1 + endif +endfunction + +function! s:find_name(lnum) + for lnum in reverse(range(1, a:lnum)) + let line = getline(lnum) + if empty(line) + return '' + endif + let name = s:extract_name(line, '-', '') + if !empty(name) + return name + endif + endfor + return '' +endfunction + +function! s:preview_commit() + if b:plug_preview < 0 + let b:plug_preview = !s:is_preview_window_open() + endif + + let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') + if empty(sha) + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) + return + endif + + if exists('g:plug_pwindow') && !s:is_preview_window_open() + execute g:plug_pwindow + execute 'e' sha + else + execute 'pedit' sha + wincmd P + endif + setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap <silent> <buffer> q :q<cr> + wincmd p +endfunction + +function! s:section(flags) + call search('\(^[x-] \)\@<=[^:]\+:', a:flags) +endfunction + +function! s:format_git_log(line) + let indent = ' ' + let tokens = split(a:line, nr2char(1)) + if len(tokens) != 5 + return indent.substitute(a:line, '\s*$', '', '') + endif + let [graph, sha, refs, subject, date] = tokens + let tag = matchstr(refs, 'tag: [^,)]\+') + let tag = empty(tag) ? ' ' : ' ('.tag.') ' + return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date) +endfunction + +function! s:append_ul(lnum, text) + call append(a:lnum, ['', a:text, repeat('-', len(a:text))]) +endfunction + +function! s:diff() + call s:prepare() + call append(0, ['Collecting changes ...', '']) + let cnts = [0, 0] + let bar = '' + let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)') + call s:progress_bar(2, bar, len(total)) + for origin in [1, 0] + let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))')))) + if empty(plugs) + continue + endif + call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') + for [k, v] in plugs + let branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap <silent> <buffer> <plug>(plug-preview) :silent! call <SID>preview_commit()<cr> + if empty(maparg("\<cr>", 'n')) + nmap <buffer> <cr> <plug>(plug-preview) + endif + if empty(maparg('o', 'n')) + nmap <buffer> o <plug>(plug-preview) + endif + endif + if cnts[0] + nnoremap <silent> <buffer> X :call <SID>revert()<cr> + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = has_key(g:plugs[name], 'commit') ? g:plugs[name].commit : s:git_revision(g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@<!,') +endfunction + +let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, '')) +let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, '')) + +if exists('g:plugs') + let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs)) + call s:upgrade_specs() + call s:define_commands() +endif + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/.local/share/nvim/site/autoload/statusbar.vim b/.local/share/nvim/site/autoload/statusbar.vim new file mode 100644 index 0000000..59e63f5 --- /dev/null +++ b/.local/share/nvim/site/autoload/statusbar.vim @@ -0,0 +1,20 @@ + +function! statusbar#refresh() + let g:currentmode={ + \ 'n' : 'NORMAL ', + \ 'v' : 'VISUAL ', + \ 'V' : 'V·Line ', + \ "\<C-V>" : 'V·Block ', + \ 'i' : 'INSERT ', + \ 'R' : 'R ', + \ 'Rv' : 'V·Replace ', + \ 'c' : 'Command ', + \} + + set statusline= + set statusline+=\ %{toupper(g:currentmode[mode()])} + set statusline+=%= + set statusline+=\ %f + set statusline+=%= + set statusline+=\ %l/%L\ %p%%\ +endfunction diff --git a/.local/share/nvim/site/colors/n8.vim b/.local/share/nvim/site/colors/n8.vim new file mode 100644 index 0000000..cac3945 --- /dev/null +++ b/.local/share/nvim/site/colors/n8.vim @@ -0,0 +1,112 @@ +" Vim color file +" Maintainer: Nathan P. Reiner <nathan.p.reiner@gmail.com> +" + +set termguicolors +set background=dark + +syntax reset + +" Default UI +hi Normal ctermfg=grey guifg='lightgrey' +hi NonText ctermfg=darkgrey gui=NONE guifg='#353535' +hi LineNr cterm=NONE ctermfg=darkgreen guifg='#547f62' +set cursorline +hi CursorLine cterm=NONE gui=NONE guibg=background +hi CursorLineNr cterm=NONE ctermfg=darkgreen gui=NONE guifg='#547f62' +hi VertSplit cterm=NONE ctermfg=darkgrey ctermbg=black guifg='#252525' guibg='#252525' +hi FloatBorder guifg='darkgrey' guibg='#252525' + +" Statusline +hi StatusLine cterm=NONE ctermbg=darkgreen guibg='#547f62' guifg='#181818' +hi StatusLineNC cterm=NONE ctermbg=black guibg=background guifg='#252525' + +" Tab +hi TabLine cterm=italic ctermbg=black ctermfg=darkgrey gui=italic guifg='#999999' guibg='#252525' +hi TabLineFill cterm=bold ctermbg=black ctermfg=darkgrey guifg='#252525' guibg='#252525' +hi TabLineSel cterm=bold ctermbg=black ctermfg=darkgrey guibg=background guifg='#ffffff' + +" Highlight +hi Visual ctermbg=235 guibg='#252525' + +" Text +hi Title cterm=bold ctermfg=brown ctermbg=black guibg=background guifg='brown' + +" Tooltip +hi Tooltip ctermbg=black guibg=background +hi Menu ctermbg=black guibg=background + +" Menu +hi Pmenu ctermbg=234 ctermfg=darkgreen guifg='darkgrey' guibg='#252525' +hi PmenuSel cterm=reverse ctermbg=black ctermfg=darkgreen guibg='#353535' guifg='lightgrey' +hi PmenuSbar ctermbg=darkgreen ctermfg=black guifg='#547f62' guibg=background +hi PmenuThumb ctermbg=black guibg='#252525' + +" Search +hi Search ctermbg=green ctermfg=black guifg='#181818' guibg='#547f62' + +" Programming GUI +hi Type cterm=italic ctermfg=green gui=italic guifg='#547f62' +hi Comment ctermfg=darkgrey gui=italic guifg='#8f8f8f' +hi ColorColumn ctermbg=darkgrey guibg='#292929' +hi identifier cterm=NONE ctermfg=grey guifg='lightgrey' +hi Error cterm=italic ctermfg=darkred ctermbg=black guibg='#181818' guifg='#bc5858' +hi Constant cterm=italic ctermfg=darkblue guifg='#588cbc' +hi String cterm=italic ctermfg=darkgreen gui=italic guifg='#7ace6d' +hi Character cterm=italic ctermfg=darkgreen gui=italic guifg='#7ace6d' +hi Number cterm=NONE ctermfg=magenta guifg='#6da6ce' +hi Boolean cterm=italic ctermfg=yellow gui=italic guifg='#ce926d' +hi PreProc ctermfg=darkgreen guifg='#b97abf' +hi Statement cterm=italic ctermfg=darkmagenta gui=italic guifg='#e8c75c' +hi Todo cterm=italic ctermbg=black ctermfg=darkyellow gui=italic guibg=background guifg='#ccae4b' + +" Ycm Error and Warnings +hi SignColumn ctermbg=black guibg=background + +hi YcmErrorLine cterm=NONE ctermbg=black ctermfg=NONE guibg=background guifg=NONE +hi YcmErrorSign cterm=NONE ctermbg=black ctermfg=darkred guifg='#bc5858' +hi YcmErrorSection cterm=underline,italic ctermbg=black ctermfg=darkred gui=underline,italic guifg='#bc5858' guibg=background +hi YcmWarningLine cterm=NONE ctermbg=black ctermfg=NONE guibg=NONE guifg=NONE +hi YcmWarningSign cterm=NONE ctermbg=black ctermfg=yellow guibg=background guifg='#e8c75c' +hi YcmWarningSection cterm=underline,italic ctermbg=black ctermfg=darkyellow gui=underline,italic guibg=background guifg='#e8c75c' + +" Git +hi GitGutterAdd cterm=NONE ctermbg=black ctermfg=green guibg=background guifg='#547f62' +hi GitGutterDelete cterm=NONE ctermbg=black ctermfg=red guibg=background guifg='#bc5858' +hi GitGutterChange cterm=NONE ctermbg=black ctermfg=darkyellow guibg=background guifg='#e8815c' + +" Spell +hi SpellBad cterm=underline ctermbg=black ctermfg=darkred guibg=background guifg='#bc5858' + +" NerdTree +hi Directory cterm=NONE ctermbg=black ctermfg=darkcyan guibg=background guifg='#5c94b2' + +" Markdown +hi markdownH1 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownH2 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownH3 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownH4 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownH5 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownH6 cterm=bold,italic ctermbg=black ctermfg=brown +hi markdownBlockquote cterm=NONE ctermbg=black ctermfg=darkgrey +hi markdownCode cterm=italic ctermbg=black ctermfg=white +hi markdownCodeDelimiter cterm=italic ctermbg=black ctermfg=darkgrey + +" EndOfBuffer +hi EndOfBuffer guifg='#5c94b2' + +" Folds +hi Folded gui=italic,bold guifg='#ce7342' guibg=background + +" Terminal +let g:terminal_color_0 = '#181818' +let g:terminal_color_1 = '#fd4d4d' +let g:terminal_color_2 = '#547f62' +let g:terminal_color_3 = '#e8d063' +let g:terminal_color_4 = '#7698c4' +let g:terminal_color_5 = '#f386de' +let g:terminal_color_6 = '#06989a' +let g:terminal_color_7 = '#d3d7cf' + +" Conceal +hi Conceal guifg=NONE guibg=NONE @@ -0,0 +1,2 @@ +export ZDOTDIR=$HOME/.config/zsh +export HISTFILE="$XDG_CACHE_HOME"/zsh/history diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..57a9693 --- /dev/null +++ b/install.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +cp -r -u -f -v .config $HOME/ +cp -r -u -f -v .local $HOME/ +cp -r -u -f -v .zshenv $HOME |