class Object
Constants
- BLUE
- BOLD
- BOLD_OFF
- CLEAR
- CURMARK
- CURSOR_COLOR
- GMARK
CONSTANTS
- GREEN
- KEY_DOWN
- KEY_END
- KEY_F1
- KEY_F10
- KEY_F5
- KEY_F6
- KEY_F7
- KEY_F8
- KEY_F9
- KEY_HOME
I needed to replace the O with a [ for this to work in Vim Home comes as ^[OH whereas on the command line it is correct as ^[[H
- KEY_LEFT
- KEY_PGDN
- KEY_PGUP
- KEY_RIGHT
- KEY_S_F1
- KEY_UP
- ON_BLUE
- ON_RED
- RED
- REVERSE
- SPACE
- UNDERLINE
- YELLOW
Public Instance Methods
TODO option of headers run query and put into a temp table and view it using vim If no outputfile name passed, then use temp table What about STDOUT TODO use temp file, format it there and append to given file only after termtable
# File lib/cetusql/cli_sqlite.rb, line 53 def OLDview_data db, sql, options outputfile = options[:output_to] formatting = options[:formatting] headers = options[:headers] #str = db.get_data sql rs = db.execute_query sql str = rs.content columns = rs.columns #puts "SQL: #{sql}.\nstr: #{str.size}" data = [] if headers data << columns.join("\t") end str.each {|line| data << line.join("\t"); } #puts "Rows: #{data.size}" require 'tempfile' tmpfile = Tempfile.new('SQL.XXXXXX') filename = tmpfile.path filename = Shellwords.escape(filename) #puts "Writing to #{filename}" tmpfile.write(data.join("\n")) tmpfile.close # need to flush, otherwise write is buffered headerstr=nil if formatting headerstr = "-H" unless headers # sometimes this can be slow, and it can fault on UTF-8 chars system("cat #{filename} | term-table.rb #{headerstr} | sponge #{filename}") end if outputfile #puts "comes here" system("cp #{filename} #{outputfile}") filename = outputfile end system "wc -l #{filename}" if $opt_debug #system "$EDITOR #{filename}" system "vim -c ':set nowrap' #{filename}" tmpfile.close tmpfile.unlink end
# File lib/cetusql/cli_utils.rb, line 313 def agree(prompt="") x = input(prompt) return true if x.upcase == "Y" false end
# File lib/cetusql/cli_sqlite.rb, line 200 def calculate_column_width content, col, maxrows=99 ret = 1 ctr = 0 content.each_with_index { |r, i| break if ctr > maxrows ctr += 1 c = r[col] x = c.to_s.length ret = x if x > ret } ret end
# File lib/cetusql/cli_sqlite.rb, line 195 def calculate_column_widths content, maxrows=99 widths = [] content.first.each_with_index {|r,i| widths << calculate_column_width(content, i, maxrows) } return widths end
print in columns ary - array of data sz - lines in one column
# File lib/cetusql/cli_utils.rb, line 325 def columnate ary, sz buff=Array.new return buff if ary.nil? || ary.size == 0 # determine width based on number of files to show # if less than sz then 1 col and full width # wid = 30 ars = ary.size ars = [$pagesize, ary.size].min d = 0 if ars <= sz wid = $gcols - d else tmp = (ars * 1.000/ sz).ceil wid = $gcols / tmp - d end #elsif ars < sz * 2 #wid = $gcols/2 - d #elsif ars < sz * 3 #wid = $gcols/3 - d #else #wid = $gcols/$gviscols - d #end # ix refers to the index in the complete file list, wherease we only show 60 at a time ix=0 while true ## ctr refers to the index in the column ctr=0 while ctr < sz f = ary[ix] fsz = f.size if fsz > wid f = f[0, wid-2]+"$ " ## we do the coloring after trunc so ANSI escpe seq does not get get if ix + $sta == $cursor f = "#{CURSOR_COLOR}#{f}#{CLEAR}" end else ## we do the coloring before padding so the entire line does not get padded, only file name if ix + $sta == $cursor f = "#{CURSOR_COLOR}#{f}#{CLEAR}" end #f = f.ljust(wid) f << " " * (wid-fsz) end if buff[ctr] buff[ctr] += f else buff[ctr] = f end ctr+=1 ix+=1 break if ix >= ary.size end break if ix >= ary.size end return buff end
connect to given database, and if no name supplied then allow user to choose
# File lib/cetusql/cli_sqlite.rb, line 27 def connect dbname=nil dbname ||= getdbname return nil unless dbname #$log.debug "XXX: CONNECT got #{dbname} " $current_db = dbname $db = SQLite3::Database.new(dbname) if dbname return $db end
allows user to select from list, returning string if user pressed ENTER
Aborts if user presses Q or C-c or ESCAPE
# File lib/cetusql/cli_utils.rb, line 218 def ctrlp arr patt = nil curr = 0 while true # clear required otherwise will keep drawing itself system("clear") if patt and patt != "" # need fuzzy match here view = arr.grep(/^#{patt}/) view = view | arr.grep(/#{patt}/) fuzzypatt = patt.split("").join(".*") view = view | arr.grep(/#{fuzzypatt}/) else view = arr end curr = [view.size-1, curr].min # if empty then curr becomes -1 curr = 0 if curr < 0 view.each_with_index do |a, i| mark = " " mark = ">" if curr == i print "#{mark} #{a} \n" end #puts " " print "\r#{patt} >" ch = get_char if ch =~ /^[a-z]$/ patt ||= "" patt << ch elsif ch == "BACKSPACE" if patt && patt.size > 0 patt = patt[0..-2] end elsif ch == "Q" or ch == "C-c" or ch == "ESCAPE" break elsif ch == "UP" curr -= 1 curr = 0 if curr < 0 elsif ch == "DOWN" curr += 1 curr = [view.size-1, curr].min # if empty then curr becomes -1 curr = 0 if curr < 0 elsif ch == "ENTER" return view[curr] else # do right and left arrow # get arrow keys here end end end
# File lib/cetusql/cli_utils.rb, line 206 def editline array Readline::HISTORY.push(*array) begin command = Readline::readline('>', true) rescue Exception => e return nil end return command end
get a character from user and return as a string Adapted from:
Need to take complex keys and matc against a hash.
# File lib/cetusql/cli_utils.rb, line 70 def get_char begin system("stty raw -echo 2>/dev/null") # turn raw input on c = nil #if $stdin.ready? c = $stdin.getc cn=c.ord return "ENTER" if cn == 10 || cn == 13 return "BACKSPACE" if cn == 127 return "C-SPACE" if cn == 0 return "SPACE" if cn == 32 # next does not seem to work, you need to bind C-i return "TAB" if cn == 8 if cn >= 0 && cn < 27 x= cn + 96 return "C-#{x.chr}" end if c == '' buff=c.chr while true k = nil if $stdin.ready? k = $stdin.getc #puts "got #{k}" buff += k.chr else x=$kh[buff] return x if x #puts "returning with #{buff}" if buff.size == 2 ## possibly a meta/alt char k = buff[-1] return "M-#{k.chr}" end return buff end end end #end return c.chr if c ensure #system "stty -raw echo" # turn raw input off system("stty -raw echo 2>/dev/null") # turn raw input on end end
# File lib/cetusql/cli_sqlite.rb, line 23 def get_column_names tbname get_metadata tbname end
# File lib/cetusql/cli_sqlite.rb, line 35 def get_data db, sql #$log.debug "SQL: #{sql} " $columns, *rows = db.execute2(sql) #$log.debug "XXX COLUMNS #{sql}, #{rows.count} " content = rows return nil if content.nil? or content[0].nil? $datatypes = content[0].types #if @datatypes.nil? return content end
# File lib/cetusql/cli_sqlite.rb, line 44 def get_metadata table get_data "select * from #{table} limit 1" return $columns end
# File lib/cetusql/cli_sqlite.rb, line 16 def get_table_names db #raise "No database file selected." unless $current_db tables = get_data "select name from sqlite_master where type='table'" tables.collect!{|x| x[0] } ## 1.9 hack, but will it run on 1.8 ?? tables end
readline version of gets
# File lib/cetusql/cli_utils.rb, line 303 def input(prompt="", newline=false) prompt += "\n" if newline ret = nil begin ret = Readline.readline(prompt, true).squeeze(" ").strip rescue Exception => e return nil end return ret end
# File lib/cetusql/cli_sqlite.rb, line 139 def mktemp require 'tempfile' tmpfile = Tempfile.new('SQL.XXXXXX') filename = tmpfile.path filename = Shellwords.escape(filename) #puts "Writing to #{filename}" #tmpfile.write(data.join("\n")) #tmpfile.close # need to flush, otherwise write is buffered return tmpfile end
multiselect from an array using fzf
# File lib/cetusql/cli_utils.rb, line 196 def multi_select title, array arr = %x[ echo "#{array.join("\n")}" | fzf --multi --reverse --prompt="#{title} >"] return arr.split("\n") end
# File lib/cetusql/cli_utils.rb, line 175 def pause text=" Press a key ..." print text get_char end
# File lib/cetusql/cli_utils.rb, line 162 def pbold text puts "#{BOLD}#{text}#{BOLD_OFF}" end
# File lib/cetusql/cli_utils.rb, line 168 def perror text pred text get_char end
# File lib/cetusql/cli_utils.rb, line 165 def pgreen text puts "#{GREEN}#{text}#{CLEAR}" end
# File lib/cetusql/cli_utils.rb, line 172 def pred text puts "#{RED}#{text}#{CLEAR}" end
print an array in columns default number of columns is 3 and can be supplied. I actually much prefer that this be derived, so we can get more columns if possible, and te width should be caclulated too
# File lib/cetusql/cli_utils.rb, line 391 def print_in_cols a, noc=nil unless noc noc = 3 if a.size < 7 noc = 1 elsif a.size < 15 noc = 2 end end x = noc - 1 cols = a.each_slice((a.size+x)/noc).to_a # todo width should be determined based on COLS of screen, and width of data cols.first.zip( *cols[1..-1] ).each{|row| puts row.map{|e| e ? '%-30s' % e : ' '}.join(" ") } end
— end constants
check screen size and accordingly adjust some variables
# File lib/cetusql/cli_utils.rb, line 293 def screen_settings $glines=%x(tput lines).to_i $gcols=%x(tput cols).to_i $grows = $glines - 3 $pagesize = 60 #$gviscols = 3 $pagesize = $grows * $gviscols end
alternative of menu that takes an array and uses numbers as indices. Hey wait, if there aer more than 10 then we are screwed since we take one character I have handled this somewhere, should check, maybe we should use characters returns text, can be nil if selection not one of choices How do we communicate to caller, that user pressed C-c
# File lib/cetusql/cli_utils.rb, line 184 def select_from title, array h = {} array.each_with_index {|e,ix| ix += 1; h[ix.to_s] = e } ch, text = menu title, h unless text if ch == "ENTER" return array.first end end return text end
single select from an array using fzf CAUTION: this messes with single and double quotes, so don't pass a query in
# File lib/cetusql/cli_utils.rb, line 202 def single_select title, array str = %x[ echo "#{array.join("\n")}" | fzf --reverse --prompt="#{title} >" -1 -0 ] return str end
given content returned by get_data
, formats and returns in a file
# File lib/cetusql/cli_sqlite.rb, line 150 def tabulate content, options data = [] content.each {|line| data << line.join("\t"); } puts "Rows: #{data.size}" if $opt_verbose require 'tempfile' tmpfile = Tempfile.new('SQL.XXXXXX') filename = tmpfile.path #filename = Shellwords.escape(filename) #puts "Writing to #{filename}" tmpfile.write(data.join("\n")) tmpfile.close # need to flush, otherwise write is buffered if options[:formatting] system("term-table.rb < #{filename} | sponge #{filename}") end return filename end
rather than use external program with dependencies, we generate tabular format for an array with headings returns filename TODO check for headings true
# File lib/cetusql/cli_sqlite.rb, line 170 def tabulate2 content, options widths = calculate_column_widths(content, 99) str = "| " sep = "+" widths.each do |w| str << "%-#{w}s | " sep << ("-"*(w+2)) + "+" end data = [] data << sep content.each_with_index {|line, ix| data << str % line data << sep if ix == 0 } data << sep require 'tempfile' tmpfile = Tempfile.new('SQL.XXXXXX') filename = tmpfile.path #filename = Shellwords.escape(filename) #puts "Writing to #{filename}" tmpfile.write(data.join("\n")) tmpfile.close # need to flush, otherwise write is buffered return filename end
edit a variable inline like zsh's vared
# File lib/cetusql/cli_utils.rb, line 15 def vared var, prompt=">" Readline.pre_input_hook = -> do Readline.insert_text var Readline.redisplay # Remove the hook right away. Readline.pre_input_hook = nil end begin input = Readline.readline(prompt, false) rescue Exception => e return nil end input end
# File lib/cetusql/cli_sqlite.rb, line 93 def view_data db, sql, options outputfile = options[:output_to] formatting = options[:formatting] headers = options[:headers] #str = db.get_data sql rs = db.execute_query sql str = rs.content columns = rs.columns #puts "SQL: #{sql}.\nstr: #{str.size}" #data = [] #if headers #data << columns.join("\t") #end #str.each {|line| data << line.join("\t"); } #puts "Rows: #{data.size}" headerstr=nil tmpfile = nil if formatting if headers str.unshift(columns) end filename = tabulate2 str, options else data = [] if headers data << columns.join("\t") end str.each {|line| data << line.join("\t"); } tmpfile = mktemp() tmpfile.write(data.join("\n")) tmpfile.close # need to flush, otherwise write is buffered filename = tmpfile.path end if outputfile #puts "comes here" system("cp #{filename} #{outputfile}") filename = outputfile end #system "$EDITOR #{filename}" system "vim -c ':set nowrap' #{filename}" if tmpfile tmpfile.close tmpfile.unlink end end