From e5c913444f35f37f820c9c1e52a2ddc61b9d72d0 Mon Sep 17 00:00:00 2001 From: Paul-Henri Froidmont Date: Sat, 22 Feb 2020 03:19:35 +0100 Subject: [PATCH] Add ranger --- configs/cli.nix | 12 + configs/files/ranger/commands.py | 213 +++++++++++++ configs/files/ranger/rc.conf | 518 +++++++++++++++++++++++++++++++ configs/files/ranger/rifle.conf | 233 ++++++++++++++ configs/files/ranger/scope.sh | 120 +++++++ 5 files changed, 1096 insertions(+) create mode 100755 configs/files/ranger/commands.py create mode 100755 configs/files/ranger/rc.conf create mode 100755 configs/files/ranger/rifle.conf create mode 100755 configs/files/ranger/scope.sh diff --git a/configs/cli.nix b/configs/cli.nix index 30e6d70..c431884 100644 --- a/configs/cli.nix +++ b/configs/cli.nix @@ -2,6 +2,7 @@ { home.packages = with pkgs; [ zsh-syntax-highlighting + ranger ]; programs.neovim = { enable = true; @@ -70,4 +71,15 @@ } ]; }; + home.file.".config/ranger" = { + source = ./files/ranger; + recursive = true; + }; + home.file.".config/ranger/plugins" = { + source = builtins.fetchGit { + url = "git://github.com/alexanderjeurissen/ranger_devicons.git"; + rev = "1fa1d0f29047979b9ffd541eb330756ac4b348ab"; + }; + recursive = true; + }; } \ No newline at end of file diff --git a/configs/files/ranger/commands.py b/configs/files/ranger/commands.py new file mode 100755 index 0000000..d6e8131 --- /dev/null +++ b/configs/files/ranger/commands.py @@ -0,0 +1,213 @@ +# This is a sample commands.py. You can add your own commands here. +# +# Please refer to commands_full.py for all the default commands and a complete +# documentation. Do NOT add them all here, or you may end up with defunct +# commands when upgrading ranger. + +# You always need to import ranger.api.commands here to get the Command class: +from ranger.api.commands import * + +# A simple command for demonstration purposes follows. +#------------------------------------------------------------------------------ + +# You can import any python module as needed. +import os + +# Any class that is a subclass of "Command" will be integrated into ranger as a +# command. Try typing ":my_edit" in ranger! +class my_edit(Command): + # The so-called doc-string of the class will be visible in the built-in + # help that is accessible by typing "?c" inside ranger. + """:my_edit + + A sample command for demonstration purposes that opens a file in an editor. + """ + + # The execute method is called when you run this command in ranger. + def execute(self): + # self.arg(1) is the first (space-separated) argument to the function. + # This way you can write ":my_edit somefilename". + if self.arg(1): + # self.rest(1) contains self.arg(1) and everything that follows + target_filename = self.rest(1) + else: + # self.fm is a ranger.core.filemanager.FileManager object and gives + # you access to internals of ranger. + # self.fm.thisfile is a ranger.container.file.File object and is a + # reference to the currently selected file. + target_filename = self.fm.thisfile.path + + # This is a generic function to print text in ranger. + self.fm.notify("Let's edit the file " + target_filename + "!") + + # Using bad=True in fm.notify allows you to print error messages: + if not os.path.exists(target_filename): + self.fm.notify("The given file does not exist!", bad=True) + return + + # This executes a function from ranger.core.acitons, a module with a + # variety of subroutines that can help you construct commands. + # Check out the source, or run "pydoc ranger.core.actions" for a list. + self.fm.edit_file(target_filename) + + # The tab method is called when you press tab, and should return a list of + # suggestions that the user will tab through. + def tab(self): + # This is a generic tab-completion function that iterates through the + # content of the current directory. + return self._tab_directory_content() + + +# https://github.com/ranger/ranger/wiki/Integrating-File-Search-with-fzf +# Now, simply bind this function to a key, by adding this to your ~/.config/ranger/rc.conf: map fzf_select +class fzf_select(Command): + """ + :fzf_select + + Find a file using fzf. + + With a prefix argument select only directories. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + # match only directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + else: + # match files and directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) +# fzf_locate +class fzf_locate(Command): + """ + :fzf_locate + + Find a file using fzf. + + With a prefix argument select only directories. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + command="locate home media | fzf -e -i" + else: + command="locate home media | fzf -e -i" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) + +class fzf_bring(Command): + """ + :fzf_bring + + Find a file using fzf and bring it to the current directory. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + # match only directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + else: + # match files and directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) + + +import os +from ranger.core.loader import CommandLoader + +class compress(Command): + def execute(self): + """ Compress marked files to current directory """ + cwd = self.fm.thisdir + marked_files = cwd.get_selection() + + if not marked_files: + return + + def refresh(_): + cwd = self.fm.get_directory(original_path) + cwd.load_content() + + original_path = cwd.path + parts = self.line.split() + au_flags = parts[1:] + + descr = "compressing files in: " + os.path.basename(parts[1]) + obj = CommandLoader(args=['apack'] + au_flags + \ + [os.path.relpath(f.path, cwd.path) for f in marked_files], descr=descr) + + obj.signal_bind('after', refresh) + self.fm.loader.add(obj) + + def tab(self): + """ Complete with current folder name """ + + extension = ['.zip', '.tar.gz', '.rar', '.7z'] + return ['compress ' + os.path.basename(self.fm.thisdir.path) + ext for ext in extension] + + + + +import os +from ranger.core.loader import CommandLoader + +class extracthere(Command): + def execute(self): + """ Extract copied files to current directory """ + copied_files = tuple(self.fm.copy_buffer) + + if not copied_files: + return + + def refresh(_): + cwd = self.fm.get_directory(original_path) + cwd.load_content() + + one_file = copied_files[0] + cwd = self.fm.thisdir + original_path = cwd.path + au_flags = ['-X', cwd.path] + au_flags += self.line.split()[1:] + au_flags += ['-e'] + + self.fm.copy_buffer.clear() + self.fm.cut_buffer = False + if len(copied_files) == 1: + descr = "extracting: " + os.path.basename(one_file.path) + else: + descr = "extracting files from: " + os.path.basename(one_file.dirname) + obj = CommandLoader(args=['aunpack'] + au_flags \ + + [f.path for f in copied_files], descr=descr) + + obj.signal_bind('after', refresh) + self.fm.loader.add(obj) diff --git a/configs/files/ranger/rc.conf b/configs/files/ranger/rc.conf new file mode 100755 index 0000000..eddebb0 --- /dev/null +++ b/configs/files/ranger/rc.conf @@ -0,0 +1,518 @@ +###SETTINGS### + +set column_ratios 1,3,4 +#set hidden_filter ^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$ +set hidden_filter ^\.|\.(?:pyc|vrb|pyo|lof|bak|swp|aux|log|nav|out|snm|toc|bcf|run\.xml|synctex\.gz|blg|bbl)$|^lost\+found$|^__(py)?cache__$ +set show_hidden false +set confirm_on_delete multiple +set preview_script ~/.config/ranger/scope.sh +set use_preview_script true +set automatically_count_files true +set open_all_images true +set vcs_aware true +set vcs_backend_git enabled +set vcs_backend_hg disabled +set vcs_backend_bzr disabled +set preview_images true +set preview_images_method w3m +set unicode_ellipsis false +set show_hidden_bookmarks false +set colorscheme default +set preview_files true +set preview_directories true +set collapse_preview true +set save_console_history false +set status_bar_on_top false +set draw_progress_bar_in_status_bar true +set draw_borders true +set dirname_in_tabs true +set mouse_enabled true +set display_size_in_main_column true +set display_size_in_status_bar true +set display_tags_in_all_columns true +set update_title false +set update_tmux_title true +set shorten_title 3 +set tilde_in_titlebar true +set max_history_size 20 +set max_console_history_size 50 +set scroll_offset 8 +set flushinput true +set padding_right true +set autosave_bookmarks false +set autoupdate_cumulative_size false +set show_cursor false +set sort natural +set sort_reverse false +set sort_case_insensitive true +set sort_directories_first true +set sort_unicode false +set xterm_alt_key false +set cd_bookmarks false +set preview_max_size 0 +set show_selection_in_titlebar true +set idle_delay 2000 +set metadata_deep_search false + + +###ALIASES### +alias e edit +alias q quit +alias q! quitall +alias qa quitall +alias qall quitall +alias setl setlocal + +alias filter scout -prt +alias find scout -aeit +alias mark scout -mr +alias unmark scout -Mr +alias search scout -rs +alias search_inc scout -rts +alias travel scout -aefiklst + + +###BASIC KEYS### + +#BASIC +map Q quit! +map q quit +copymap q ZZ ZQ + +#map R reload_cwd +map reset +#map redraw_window +map abort +map change_mode normal + +map i display_file +map ? help +#map W display_log +map w taskview_open +map S shell $SHELL + +map : console +map ; console +map ! console shell%space +map @ console -p6 shell %s +map # console shell -p%space +#map s console shell%space +map r chain draw_possible_programs; console open_with%%space +map f console find%space +map cd console cd%space + + +# Change the line mode +#map Mf linemode filename +#map Mi linemode fileinfo +#map Mp linemode permissions +#map Mt linemode metatitle + +#moc +#map Mc shell mocp -c +#map Ma shell mocp -a %s +#map Ms shell mocp -p +#map MS shell mocp -S +#map Mp shell mocp -G +#map Mn shell mocp -f +#map Mb shell mocp -r +#map MN shell mocp -s && mocp -c && mocp -a %s && mocp -p +#map Mo shell mocp -j 0%% +#map MK shell killall mocp + + + +# Tagging / Marking +map at tag_toggle +map ut tag_remove +map " tag_toggle tag=%any +map mark_files toggle=True +map va mark_files all=True toggle=True +map uv mark_files all=True val=False +map vs toggle_visual_mode +map uV toggle_visual_mode reverse=True + + + +# For the nostalgics: Midnight Commander bindings +map help +map display_file +map edit +map copy +map console shell echo "require(rmarkdown); render_site()" | R --vanilla +map cut +map console mkdir%space +map console delete +map exit + + +# In case you work on a keyboard with dvorak layout +map move up=1 +map move down=1 +map move left=1 +map move right=1 +map move to=0 +map move to=-1 +map move down=1 pages=True +map move up=1 pages=True +map move right=1 +map console delete +map console touch%space + + +# VIM-like +copymap k +copymap j +copymap h +copymap l +copymap gg +copymap G +copymap +copymap + +map J move down=0.5 pages=True +map K move up=0.5 pages=True +copymap J +copymap K + + +# Jumping around +map H history_go -1 +map L history_go 1 +map ] move_parent 1 +map [ move_parent -1 +map } traverse + +#DEFAULT MOVEMENT +map ge cd /etc +map gu cd /usr +#map gl cd -r . +map gL cd -r %f +#map gv cd /var +map gM cd /mnt +map gr cd / +map gR eval fm.cd(ranger.RANGERDIR) +map g? cd /usr/share/doc/ranger + +# Tabs +map tab_new ~ +map tab_close +map tab_move 1 +map tab_move -1 +map tab_move 1 +map tab_move -1 +#map gt tab_move 1 +#map gT tab_move -1 +map gn tab_new ~ +#map gc tab_close +map tt tab_close +map uq tab_restore +map tab_open 1 +map tab_open 2 +map tab_open 3 +map tab_open 4 +map tab_open 5 +map tab_open 6 +map tab_open 7 +map tab_open 8 +map tab_open 9 + + +# External Programs +map E edit +map du shell -p du --max-depth=1 -h --apparent-size +map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh +map yp shell -f echo -n %%d/%%f | xsel -i; xsel -o | xsel -i -b +map yd shell -f echo -n %%d | xsel -i; xsel -o | xsel -i -b +map yn shell -f echo -n %%f | xsel -i; xsel -o | xsel -i -b + + +# Filesystem Operations +map = chmod +map cw console rename%space +map aa rename_append +map A eval fm.open_console('rename ' + fm.thisfile.relative_path) +map I eval fm.open_console('rename ' + fm.thisfile.relative_path, position=7) +map pp paste +map po paste overwrite=True +map pP paste append=True +map pO paste overwrite=True append=True +map pl paste_symlink relative=False +map pL paste_symlink relative=True +map phl paste_hardlink +map pht paste_hardlinked_subtree + +map dD console delete + +map dd cut +map ud uncut +map da cut mode=add +map dr cut mode=remove + +map yy copy +map uy uncut +map ya copy mode=add +map yr copy mode=remove + + +# Temporary workarounds +map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier) +map dG eval fm.cut(dirarg=dict(to=-1), narg=quantifier) +map dj eval fm.cut(dirarg=dict(down=1), narg=quantifier) +map dk eval fm.cut(dirarg=dict(up=1), narg=quantifier) +map ygg eval fm.copy(dirarg=dict(to=0), narg=quantifier) +map yG eval fm.copy(dirarg=dict(to=-1), narg=quantifier) +map yj eval fm.copy(dirarg=dict(down=1), narg=quantifier) +map yk eval fm.copy(dirarg=dict(up=1), narg=quantifier) + + +# Searching +map / console search%space +map n search_next +map N search_next forward=False +map ct search_next order=tag +map cs search_next order=size +map ci search_next order=mimetype +map cc search_next order=ctime +map cm search_next order=mtime +map ca search_next order=atime + + +# Sorting +map or toggle_option sort_reverse +map oz set sort=random +map os chain set sort=size; set sort_reverse=False +map ob chain set sort=basename; set sort_reverse=False +map on chain set sort=natural; set sort_reverse=False +map om chain set sort=mtime; set sort_reverse=False +map oc chain set sort=ctime; set sort_reverse=False +map oa chain set sort=atime; set sort_reverse=False +map ot chain set sort=type; set sort_reverse=False +map oe chain set sort=extension; set sort_reverse=False + +map oS chain set sort=size; set sort_reverse=True +map oB chain set sort=basename; set sort_reverse=True +map oN chain set sort=natural; set sort_reverse=True +map oM chain set sort=mtime; set sort_reverse=True +map oC chain set sort=ctime; set sort_reverse=True +map oA chain set sort=atime; set sort_reverse=True +map oT chain set sort=type; set sort_reverse=True +map oE chain set sort=extension; set sort_reverse=True + +map dc get_cumulative_size + + +# Settings +map zc toggle_option collapse_preview +map zd toggle_option sort_directories_first +map zh toggle_option show_hidden +map toggle_option show_hidden +map zi toggle_option flushinput +map zm toggle_option mouse_enabled +map zp toggle_option preview_files +map zP toggle_option preview_directories +map zs toggle_option sort_case_insensitive +map zu toggle_option autoupdate_cumulative_size +map zv toggle_option use_preview_script +map zf console filter%space + + +# Bookmarks +#map ` enter_bookmark %any +#map ' enter_bookmark %any +#map mm set_bookmark %any +#map um unset_bookmark %any + +#map m draw_bookmarks +#copymap m um ` ' + + +# Generate all the chmod bindings with some python help: +eval for arg in "rwxXst": cmd("map +u{0} shell -f chmod u+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +g{0} shell -f chmod g+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +o{0} shell -f chmod o+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +a{0} shell -f chmod a+{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map +{0} shell -f chmod u+{0} %s".format(arg)) + +eval for arg in "rwxXst": cmd("map -u{0} shell -f chmod u-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -g{0} shell -f chmod g-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -o{0} shell -f chmod o-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -a{0} shell -f chmod a-{0} %s".format(arg)) +eval for arg in "rwxXst": cmd("map -{0} shell -f chmod u-{0} %s".format(arg)) + + +###CONSOLE KEYS### +# Basic +cmap eval fm.ui.console.tab() +cmap eval fm.ui.console.tab(-1) +cmap eval fm.ui.console.close() +cmap eval fm.ui.console.execute() +#cmap redraw_window + +copycmap +copycmap + + +# Move around +cmap eval fm.ui.console.history_move(-1) +cmap eval fm.ui.console.history_move(1) +cmap eval fm.ui.console.move(left=1) +cmap eval fm.ui.console.move(right=1) +cmap eval fm.ui.console.move(right=0, absolute=True) +cmap eval fm.ui.console.move(right=-1, absolute=True) + + +# Line Editing +cmap eval fm.ui.console.delete(-1) +cmap eval fm.ui.console.delete(0) +cmap eval fm.ui.console.delete_word() +cmap eval fm.ui.console.delete_word(backward=False) +cmap eval fm.ui.console.delete_rest(1) +cmap eval fm.ui.console.delete_rest(-1) +cmap eval fm.ui.console.paste() + +# And of course the emacs way +#copycmap +#copycmap +#copycmap +#copycmap +#copycmap +#copycmap +#copycmap +#copycmap + + +# Note: There are multiple ways to express backspaces. (code 263) +# and (code 127). To be sure, use both. +copycmap + +# This special expression allows typing in numerals: +cmap false + + + +###PAGER KEYS### +# Movement +pmap pager_move down=1 +pmap pager_move up=1 +pmap pager_move left=4 +pmap pager_move right=4 +pmap pager_move to=0 +pmap pager_move to=-1 +pmap pager_move down=1.0 pages=True +pmap pager_move up=1.0 pages=True +pmap pager_move down=0.5 pages=True +pmap pager_move up=0.5 pages=True + +copypmap k +copypmap j +copypmap h +copypmap l +copypmap g +copypmap G +copypmap d +copypmap u +copypmap n f +copypmap p b + + +# Basic +#pmap redraw_window +pmap pager_close +copypmap q Q i +pmap E edit_file + +# =================================================================== +# == Taskview Keybindings +# =================================================================== + +# Movement +tmap taskview_move up=1 +tmap taskview_move down=1 +tmap taskview_move to=0 +tmap taskview_move to=-1 +tmap taskview_move down=1.0 pages=True +tmap taskview_move up=1.0 pages=True +tmap taskview_move down=0.5 pages=True +tmap taskview_move up=0.5 pages=True + +copytmap k +copytmap j +copytmap g +copytmap G +copytmap u +copytmap n f +copytmap p b + +# Changing priority and deleting tasks +tmap J eval -q fm.ui.taskview.task_move(-1) +tmap K eval -q fm.ui.taskview.task_move(0) +tmap dd eval -q fm.ui.taskview.task_remove() +tmap eval -q fm.ui.taskview.task_move(-1) +tmap eval -q fm.ui.taskview.task_move(0) +tmap eval -q fm.ui.taskview.task_remove() + +# Basic +#tmap redraw_window +tmap taskview_close +copytmap q Q w + + +map sp console shell bash speedvid.sh %f%space +map x shell chmod -x %s + +#General +map cW bulkrename %s +map mkd console mkdir%space +map sc console shell ln -sT%space +map D console delete +map X shell extract %f +map Z shell tar -cvzf %f.tar.gz %f +map fzf_select +map fzf_locate + +#Document Manipulation +map p1s shell lpr -o sides=one-sided %f +map p2s shell lpr -o sides=two-sided-long-edge %f +map MP shell pandoc %f -o %f.pdf +map MX shell xelatex %f +map ML shell latex %f +map TC shell texclear +map Txa console shell cp ~/Documents/LaTeX/article.tex%space +map Txs console shell cp ~/Documents/LaTeX/beamer.tex%space +map Txh console shell cp ~/Documents/LaTeX/handout.tex%space + +#Image commands +map bg shell cp %f ~/.config/wall.png && feh --bg-scale %f +map bw shell wal -c -i %f && cp %f ~/.config/wall.png +map C shell killall w3mimgdisplay && convert -rotate 90 %s %s +map F shell killall w3mimgdisplay && convert -flop %s %s +map bl shell killall w3mimgdisplay && convert %s -resize 1440x1080\> bl_%s +map TR shell convert %s -transparent white %s + +#Music (mpd) shortcuts +map MS shell mpd +map MK shell killall mpd +map Ma shell mpc add "%s" +map Ms shell mpc play +map Mp shell mpc toggle +map Mn shell mpc next +map Mb shell mpc prev +map MN shell mpc stop && mpc clear && mpc add "%s" +map Mo shell mpc seek 0% + +#Audio tagging (Requires eyeD3) +map Ta eval fm.open_console('shell eyeD3 -a ' + fm.thisfile.relative_path, position=15) +#Artist +map TA eval fm.open_console('shell eyeD3 -A ' + fm.thisfile.relative_path, position=15) +#Album +map Tb eval fm.open_console('shell eyeD3 -b ' + fm.thisfile.relative_path, position=15) +#Album artist +map Tt eval fm.open_console('shell eyeD3 -t "" ' + fm.thisfile.relative_path, position=16) +map Tn eval fm.open_console('shell eyeD3 -n "" ' + fm.thisfile.relative_path, position=16) + +#Downloading +map ytv console shell youtube-dl -ic%space +map yta console shell youtube-dl -xic%space + +default_linemode devicons \ No newline at end of file diff --git a/configs/files/ranger/rifle.conf b/configs/files/ranger/rifle.conf new file mode 100755 index 0000000..a31aabe --- /dev/null +++ b/configs/files/ranger/rifle.conf @@ -0,0 +1,233 @@ +# vim: ft=cfg +# +# This is the configuration file of "rifle", ranger's file executor/opener. +# Each line consists of conditions and a command. For each line the conditions +# are checked and if they are met, the respective command is run. +# +# Syntax: +# , , ... = command +# +# The command can contain these environment variables: +# $1-$9 | The n-th selected file +# $@ | All selected files +# +# If you use the special command "ask", rifle will ask you what program to run. +# +# Prefixing a condition with "!" will negate its result. +# These conditions are currently supported: +# match | The regexp matches $1 +# ext | The regexp matches the extension of $1 +# mime | The regexp matches the mime type of $1 +# name | The regexp matches the basename of $1 +# path | The regexp matches the absolute path of $1 +# has | The program is installed (i.e. located in $PATH) +# env | The environment variable "variable" is non-empty +# file | $1 is a file +# directory | $1 is a directory +# number | change the number of this command to n +# terminal | stdin, stderr and stdout are connected to a terminal +# X | $DISPLAY is not empty (i.e. Xorg runs) +# +# There are also pseudo-conditions which have a "side effect": +# flag | Change how the program is run. See below. +# label