- container/
- row/
- span4/
- h2/
- - icon/051_eye_open
+ - icon/eye-open
- Screencasts
- p/
Check out the
- to see Xiki in action.
+ to see Xiki in action. This is the best way to get a feel for it.
- span4/
- h2/
+ icon/
- - 154_show_big_thumbnails
- - Other Tools
+ - th
+ - Creating
- p/
| Create menus to access other tools just by typing.
| If the menu doesn't exist, xiki guides you through creating it.
- span4/
- h2/
- - icon/160_imac
+ - icon/desktop
Shell + GUI
- p/
Xiki merges shell and GUI concepts.
@@ -79,7 +111,7 @@ bootstrap/
- row/
- span4/
- h2/
- - icon/012_heart
+ - icon/heart
- Code
- p/
Go to
@@ -87,7 +119,7 @@ bootstrap/
to check out the code and install Xiki.
- span4/
- h2/
- - icon/043_group
+ - icon/group
- Mailing list
- p/
See the
@@ -95,7 +127,7 @@ bootstrap/
for help getting Xiki installed, or to ask other questions or share ideas.
- span4/
- h2/
- - icon/108_left_indent
+ - icon/twitter
Twitter
-
Follow
@@ -131,6 +163,48 @@ bootstrap/
| })();
+> 2. Deploy
+:p/xiki.org/www/
+/projects/xiki.org/www/
+ - 1) Expand the above 'xiki loc' command
+ - 2) Copy to the server
+ $ scp index.html xiki@xiki.org:/var/www/xiki.org/index.html
+ - 3) Verify
+ =http://xiki.com
+
+
+ + index.html
+
+ + screencasts/
+ + index.shtml
+ $ scp index.shtml xiki@xiki.org:/var/www/xiki.org/screencasts/index.shtml
+/tmp/
+ $ scp tmp.html xiki@xiki.org:/var/www/xiki.org/screencasts/index.shtml
+
+http://xiki.org/screencasts
+
+
+/xiki@xiki.org/var/www/xiki.org/
+
+
+- old
+ | /tmp/
+ | - tmp.html
+ | $ scp tmp.html xiki@xiki.org:/var/www/xiki.org/index.html
+ | $ scp tmp.html xiki@xiki.org:/var/www/xiki.org/screencasts/index.shtml
+
+> old header > "Xiki is a UI Language."
+ |
+ |
+ | Xiki is a UI Language.
+ |
+ | The shell console reimagined. Type notes, prototype, and program.
+ |
+ | Donate to Xiki:
+
+
>> Old swimming shark
- move to bottom!
@@ -161,11 +235,24 @@ bootstrap/
- p/In Xiki, text is always editable. Command output is inserted underneath the command, nested two spaces.
+> Old wording
+ |
+ |
+ |
+ |
+
+
+
+
+> local url
+http://xiki.loc
+
+
> Screencasts: index
bootstrap/
- project name/Xiki
- navbar/
- |
+ |
- style/
.section {
diff --git a/roots/active_links.rb b/roots/active_links.rb
new file mode 100644
index 00000000..44a9d52b
--- /dev/null
+++ b/roots/active_links.rb
@@ -0,0 +1,126 @@
+# Used by > list+active
+
+options[:no_slash] = 1
+
+file, quotes = args.partition{|i| i !~ /^:/}
+file = file.delete_if{|o| o =~ /^[#*]/} # Remove any ##... or **... lines
+target_file = Files.tilde_for_home file.join("/")
+
+nav = Bookmarks["%links"]
+
+# /file/:foo, so navigate or handle tasks...
+
+if quotes.any?
+
+ quote = quotes[-1]
+ quote.sub! /../, '' # Remove ": "
+
+ # Handle tasks...
+
+ return "* show in links\n* highlight and show" if task == []
+ if task == ["show in links"]
+ View.open nav
+ View.to_snippet quote
+ return ""
+ end
+
+ # No tasks, so navigate to file path...
+
+ View.open target_file, :stay_in_bar=>1
+ View.to_snippet quote
+
+ if task == ["highlight and show"]
+ Color.mark "red"
+ end
+
+ return ""
+end
+
+# /file, so show quotes from ^links for this file...
+
+target_file_expanded = File.expand_path target_file
+
+limit = 35
+result, dir, filename, filename_indent = "", [], nil, nil
+heading = nil
+
+IO.foreach(nav) do |line|
+
+ line.sub! "\n", ''
+
+ indent = Tree.indent_size line
+
+ # .../, so append as dir...
+
+ if line =~ /^[^:|#]*\/$/
+ dir = dir[0..indent]
+ next dir[indent] = line
+ end
+
+ # Filename found, so add to path if it's ours...
+
+ if like_filename = line[/^ *[+-] (.+\w)$/, 1]
+
+ # Construct full path
+
+ full = dir.compact
+ full.map!{|o| Line.without_label :line=>o.strip}
+ full = full[0..indent]
+ full[indent] = like_filename
+ full = full.join
+ full = File.expand_path full
+
+ # Not target file, so clear out filename and skip
+
+ if full == target_file_expanded
+ if heading
+ result << "#{heading}\n"
+ heading = nil
+ end
+ filename = full
+ filename_indent = line[/^ */]
+ else
+ filename = nil
+ end
+
+ next
+
+ end
+
+ # Heading, so remember it...
+
+ if line =~ /^>/
+ heading = line
+ heading = nil if heading =~ /^> ?:?$/
+ next
+ end
+
+ next if ! filename
+
+ # Blank line, so just add blanks (duplicate blanks will be removed later)...
+
+ if line.blank?
+ result << "\n"
+ next
+ end
+
+ next if line !~ /^ / # Ignore stuff at beginning of line that isn't a dir
+
+ # Filename exists, so add this line...
+
+ # Subtract off of indenting the amount the file was indented
+ line.sub!(/^#{filename_indent} /, '')
+
+ result << "#{line}\n"
+
+ limit -= 1
+ break if limit < 1
+end
+
+result.gsub! /\n\n\n+/, "\n\n" # Don't allow mustiple linebreaks
+result.gsub! /\n(\n *[:+-])/, "\\1"
+
+options[:filter_dont_collapse] = 1
+
+result
+
diff --git a/lib/xiki/tools/address_book.rb b/roots/address_book.rb
similarity index 83%
rename from lib/xiki/tools/address_book.rb
rename to roots/address_book.rb
index a9d90ee8..e42f1d11 100644
--- a/lib/xiki/tools/address_book.rb
+++ b/roots/address_book.rb
@@ -2,6 +2,8 @@ module Xiki
class AddressBook
def self.names
names = Applescript.run "get the name of every person as string", :app=>"address book", :delimiter=>"|"
+ names.sub! /\A"/, ''
+ names.sub! /"\z/, ''
names.split "|"
end
@@ -16,7 +18,10 @@ def self.menu key=nil
txt.gsub! /, /, "\n"
txt.gsub! /:/, ": "
txt = txt.split("\n").select{|o| o !~ /^"\}|missing value|END: VCARD|VERSION: 3.0|BEGIN: VCARD/}.join("\n")
- txt.gsub! /^/, "- "
+ txt.sub!(/^PRODID:.+\n/, "")
+ txt.gsub! /^/, "| "
+ txt.gsub! /;+/, " "
+ txt.gsub!(/ +$/, "")
txt
end
diff --git a/roots/all.rb b/roots/all.rb
new file mode 100644
index 00000000..0bd4de6c
--- /dev/null
+++ b/roots/all.rb
@@ -0,0 +1,2 @@
+all = Xiki::Command.completions
+all.map{|o| "<< #{o}/\n"}.join("")
diff --git a/roots/amazon.rb b/roots/amazon.rb
new file mode 100644
index 00000000..71871132
--- /dev/null
+++ b/roots/amazon.rb
@@ -0,0 +1,12 @@
+line = args[0]
+
+# If no arg, prompt to type something
+
+return "=prompt/Type something to search on amazon" if ! line
+
+# If arg, look it up
+
+line = CGI.escape line
+
+Firefox.url "http://www.amazon.com/s?field-keywords=#{line}"
+nil
diff --git a/menu/animals.menu b/roots/animals.menu
similarity index 81%
rename from menu/animals.menu
rename to roots/animals.menu
index ccef1911..62676fde 100644
--- a/menu/animals.menu
+++ b/roots/animals.menu
@@ -6,7 +6,7 @@
Enjoy fetching things. Pretty much anything will do.
- pit bulls/
- @bootstrap/
+ =bootstrap/
- project name/Pit Bulls
- hero/
- h1/Pit Bulls
@@ -14,7 +14,6 @@
- row/
- span6/
- h1/
- - icon/002_dog
- Facts
- p/This is some stuff.
- span6/
@@ -22,17 +21,16 @@
- p/dolor sit lorem...
- cats/
- persian/
+ | meow
- siamese/
+ | meow
- wild/
- lions/
- - white/
- - other/
+ | growl
- tigers/
- - ligers/
- - regular type/
+ | growl
- bears/
- - brown/
- - black/
+ | oh my
- bugs/
- spiders/
- - bees/
+ | go web
diff --git a/roots/animate.rb b/roots/animate.rb
new file mode 100644
index 00000000..2f5d492a
--- /dev/null
+++ b/roots/animate.rb
@@ -0,0 +1,286 @@
+class Xiki::Animate
+ MENU = "
+ - sequences/
+ - xiki/
+ | text
+ | text
+ | text
+ | text
+ | text
+ | text
+ | text
+ | text
+ | text
+ | text
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | txet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xtet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiet
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiei
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ - xiki 2/
+ | text
+ | text
+ | text
+ | text
+ | text
+ | texi
+ | texi
+ | texi
+ | texi
+ | texi
+ | iexi
+ | iexi
+ | iexi
+ | iexi
+ | iexi
+ | ikxi
+ | ikxi
+ | ikxi
+ | ikxi
+ | ikxi
+ | ixki
+ | ixki
+ | ixki
+ | ixki
+ | ixki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ | xiki
+ - bouncing ball/
+ |~ a
+ |~ a
+ |~ a
+ |~ b
+ |~ b
+ |~ c
+ |~ d
+ |~ e
+ |~ d
+ |~ c
+ |~ b
+ |~ b
+ |~ a
+ |~ a
+ |~ a
+ |~ b
+ |~ b
+ |~ c
+ |~ d
+ |~ e
+ |~ d
+ |~ c
+ |~ c
+ |~ b
+ |~ b
+ |~ b
+ |~ c
+ |~ c
+ |~ d
+ |~ e
+ |~ d
+ |~ d
+ |~ e
+ |~ d
+ |~ e
+ - explosion/
+ |~ a
+ |~ a
+ |~ a
+ |~ b
+ |~ b
+ |~ b
+ |~ c
+ |~ c
+ |~ d
+ |~ e
+ |~ ii
+ |~ vvv
+ |~ |||v
+ |~ exnxe
+ |~ y ad c
+ |~ e c
+ |~ e
+ - gun/
+ |~ eiyrsssbbbbfb
+ |~ eiyrsssbbbbfbb
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb b
+ |~ eiyrsssbbbbfb
+ - ship/
+ |~ dd
+ |~ iidd
+ |~ iiiidd
+ |~ iiiiiidd
+ |~ |iiiiiiidd
+ |~ ii|iiiiiiidd
+ |~ viii|iiiiiiidd
+ |~ |vviii|iiiiiiidd
+ |~ i||vviii|iiiiiiidd
+ |~ iii||vviii|iiiiiiidd
+ |~ viiii||vviii|iiiiiiidd
+ |~ vvviiii||vviii|iiiiiiidd
+ |~ vvvvviiii||vviii|iiiiiiidd
+ |~ iivvvvviiii||vviii|iiiiiiidd
+ |~ iiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ |~ ddiiiivvvvviiii||vviii|iiiiiiidd
+ - space invaders/
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ |~ edgprppprpgde hlsrsssrslh
+ |~ dtprppprptd ehcsrsssrsche
+ "
+
+ def self.menu_after output, *args
+
+ # If root, add sequences
+ if args == []
+ # sequences = Tree.children self.menu.unindent, "sequences"
+ sequences = Tree.children MENU.unindent, "sequences"
+ return "#{sequences}#{output}"
+ end
+
+ return if output
+
+ sequence = args[0]
+
+ sequence = Tree.children MENU.unindent, "sequences/#{sequence}"
+
+ sequence = sequence.split "\n"
+
+ cursor = View.cursor
+ indent = Line.indent
+ Move.to_end
+ View.<< "\n", :dont_move=>1
+
+ sequence.each do |line|
+ Line.next
+ Line.sub! /.*/, " #{indent}#{line}"
+ View.cursor = cursor;
+ View.pause 0.05;
+ end
+
+ nil
+ end
+
+end
diff --git a/roots/apache.rb b/roots/apache.rb
new file mode 100644
index 00000000..ed0eae0d
--- /dev/null
+++ b/roots/apache.rb
@@ -0,0 +1,27 @@
+module Xiki
+ class Apache
+ MENU = %`
+ - .restart/
+ - .control/
+ - .restart/
+ - stop/
+ ! Shell.run "sudo apachectl stop", :dir=>"/etc/apache2/"
+ - start/
+ ! Shell.run "sudo apachectl start", :dir=>"/etc/apache2/"
+ - conf/
+ @/etc/apache2/
+ + other/
+ + httpd.conf
+ - logs/
+ =/var/log/apache2/
+ % tail -f error_log
+ % tail -f access_log
+ `
+
+ def self.restart
+ Shell.run "sudo apachectl restart", :dir=>"/etc/apache2/"
+
+ # "- restarting!"
+ end
+ end
+end
diff --git a/roots/apigee.menu b/roots/apigee.menu
new file mode 100644
index 00000000..2972277b
--- /dev/null
+++ b/roots/apigee.menu
@@ -0,0 +1,112 @@
+- about/
+ | Apigee is sort of a JSON-ish database in the cloud. You can
+ | include their js from an html page, and then use their javascript
+ | api to send json blobs to their server to store, and retrieve them.
+ - more/
+ | You can also use it from ios/android/php etc.
+ @http://apigee.com
+- examples/
+ - preparation/
+ - 1. Get a developer userid/
+ | At this url...
+ @https://developers.apigee.com/
+ - 2. Create record in 'book'/
+ - Click "Data Explorer" on this page
+ @https://apigee.com/usergrid/
+ - Click "Add Collection"
+ | Name it "books".
+ | Click the "POST" radio button.
+ - Type in this and click "Run Query"
+ | {
+ | "name":"hi"
+ | "author":"steve",
+ | "title":"hello world",
+ | }
+ - hello world/
+ - Replace "trogdoro" with your username (see preparation/).
+ - Then double-click to run.
+ @html/
+ |
+ |
+ |
+ |
+ |
+ - jquery mobile example/
+ - Replace "trogdoro" with your username (see preparation/).
+ - Then double-click to run.
+ @html/
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ | Nothing loaded yet. Maybe you haven't added a record to your "/books" collection yet?
+ |
+ |
+ |
+ |
+- docs/
+ @learn/
+ > Terms
+ | main 3 terms : collection, Data Explorer, usergrid
+ | collection : database table
+ | Data Explorer : web interface to create collection
+ | usergrid : url for Data Explorer
+
+ > Code
+ | Sign in : apigee = new Usergrid.Client({orgName:'my username', appName:'sandbox'})
+ | make collection : my_books = new Usergrid.Collection({ "client":apigee, "type":"books" })
+ | query all : my_books.fetch(success_callback, failure_callback)
+ | functions to use in fetch() : .hasNextEntity(), .getNextEntity()
+ | read value : book.get('title')
diff --git a/roots/append.rb b/roots/append.rb
new file mode 100644
index 00000000..42c66546
--- /dev/null
+++ b/roots/append.rb
@@ -0,0 +1,10 @@
+Applescript.exec("Firefox", "activate") if Keys.prefix_u
+
+return "
+ |
Type html here
+ |
To append to the browser
+ " if args.blank?
+
+return "=beg/quoted/" if args[0] !~ /\n/
+
+Firefox.append args[0]
diff --git a/roots/applescript.rb b/roots/applescript.rb
new file mode 100644
index 00000000..e7b4f23b
--- /dev/null
+++ b/roots/applescript.rb
@@ -0,0 +1,50 @@
+module Menu
+ class Applescript
+
+ MENU = %`
+ > Pass in applescript to run
+ | tell application "iTunes" to playpause
+ - docs/
+ - examples/
+ - one line/
+ =applescript/iTunes/playpause
+ - multiple lines/
+ =applescript/
+ | tell application "iTunes"
+ | playpause
+ | end
+ - terminal/
+ =applescript/Terminal/do script "pwd" in window 1
+ - api/
+ - run applescript/
+ =code/
+ | Applescript.run 'tell application "iTunes" to playpause'
+ - shortcut to wrap 'tell' in block/
+ =code/
+ | Applescript.run "playpause", :app=>"iTunes"
+ | Applescript.run "get the name of every track of library playlist 1 as string", :app=>"iTunes", :delimiter=>"|"
+ `
+
+ def self.menu_after output, *args
+
+ return output if output # MENU handled it, so don't interfere
+
+ command = args[0]
+
+ # /Application/script, so send specific app...
+
+ app, command = args if args[1]
+
+ # If only 1 line, remove colon
+ command.sub! /^: /, '' if command !~ /\n/
+
+ # /script, so run it...
+
+ result = Xiki::Applescript.run command, :app=>app
+
+ "<*"
+ end
+
+ end
+end
+
diff --git a/menu/app.rb b/roots/applications.rb
similarity index 77%
rename from menu/app.rb
rename to roots/applications.rb
index 6bf56c77..76510874 100644
--- a/menu/app.rb
+++ b/roots/applications.rb
@@ -1,7 +1,7 @@
#
# Shows each OS X app, and lets you launch them.
#
-class App
+class Applications
def self.menu name=nil
if ! name
@@ -9,7 +9,7 @@ def self.menu name=nil
return txt
end
- Console.sync "open \"/Applications/#{name}.app\""
+ Shell.sync "open \"/Applications/#{name}.app\""
nil
end
end
diff --git a/roots/args.rb b/roots/args.rb
new file mode 100644
index 00000000..78279526
--- /dev/null
+++ b/roots/args.rb
@@ -0,0 +1,66 @@
+class Xiki::Args
+
+ MENU = %`
+ - docs/
+ - summary/
+ | This menu helps you when creating your own menus. It
+ | lets you try different ways of passing stuff into the
+ | menu and see how it gets passed in as args to the menu.
+ - more/
+ | Usually only menus that have code need to worry about
+ | args. Nonetheless, trying out the examples below can
+ | be instructive regarding how items are passed into all
+ | menus.
+ - examples/
+ @args/a/
+ @args/a/b/
+ - more/
+ - items underneath/
+ @args/
+ - a/
+ @args/
+ - a/
+ - b/
+ @args/
+ - a/b/
+ - multiple lines/
+ @args/
+ : Colons mean to pass the lines in
+ : together, with the colons removed.
+ @args/
+ | Pipes mean to pass the line in individually. Unless the
+ | menu begs for all the lines by returning "=beg/siblings/".
+ | See the docs for the "beg" menu for more information.
+ - other types of items/
+ @args/
+ > Headings
+ @args/
+ | When a nested menu starts with "@", it doesn't invoke
+ | the parent menu at all.
+ @ip/
+ - slashes/
+ @args/
+ > Headings
+ @args/
+ | When a nested menu starts with "@", it doesn't invoke
+ | the parent menu at all.
+ @ip/
+ - see/
+ @options/
+ `
+
+
+ def self.menu_after output, *args
+
+ # /, so show args and docs
+ return "#{Tree.quote args.inspect}#{output}" if args == []
+
+ # /docs/..., so it was already handled
+ return nil if output
+
+ # /foo/bar..., so show user what the args are
+ Tree.quote args.inspect
+ end
+
+
+end
diff --git a/roots/bash.rb b/roots/bash.rb
new file mode 100644
index 00000000..8506d07a
--- /dev/null
+++ b/roots/bash.rb
@@ -0,0 +1,34 @@
+# /, so tell them to run stuff
+
+if args == []
+ return "
+ | # Put some bash code here to run it
+ | echo 'something'
+ | echo 'something else'
+ "
+end
+
+# tasks+, so show items
+
+return "
+ * run as script
+ * run in bash
+ " if task == []
+
+return DiffLog.quit_and_run args[0] if task == ["run in bash"]
+
+txt = args[0]
+txt.gsub!(/^\$ /, '')
+
+if task == ["run as script"]
+ # Save to script and run
+ File.open("/tmp/tmp.bash", "w") { |f| f << txt }
+ DiffLog.quit_and_run "bash /tmp/tmp.bash"
+end
+
+# /code, so run it
+
+File.open("/tmp/bash.sh", "w") { |f| f << txt }
+txt = Shell.command "bash /tmp/bash.sh"#, :stdin=>"foo"
+Tree.quote txt
+
diff --git a/roots/beg.menu b/roots/beg.menu
new file mode 100644
index 00000000..7f474d20
--- /dev/null
+++ b/roots/beg.menu
@@ -0,0 +1,101 @@
+> Things menus can return to request more info
+- docs/
+ - summary/
+ | Menus can return "=beg/siblings/" and similar things to indicate
+ | they want the Xiki client to re-invoke them with more
+ | information. Normally when you launch a menu only the path is
+ | sent as input (and a few other pieces of info).
+ |
+ | Returning something like this makes Xiki re-invoke the menu,
+ | passing in the additional information (unless there's a reason
+ | not to).
+ |
+ | See the items below for examples.
+ - source/
+ | Here's the start of the code that handles this:
+ =/projects/xiki/lib/xiki/core/launcher.rb
+ + ##if\ beg\ ==/
+ - future todo maybe/
+ - when colons, way to beg multiple lines, but with colons, to distinguish/
+ - example/
+ - when...
+ : multiple
+ : lines
+ - would return
+ ": multiple\n: lines\n"
+ - have character equivalants/
+ | So maybe these will be equivalent
+ | =beg/quoted/
+ | must be defined in code, so get path
+ path = Bookmarks["%#{args[0]}"]
+ # Make new bookmark
+ Bookmarks.set args[0], :file=>path
+ end
+
+ return "<* - will be at top next time"
+
+end
+
+# /foo, so jump to bookmark...
+
+View.open "%#{args[0]}"
+
+""
diff --git a/menu/bootstrap.rb b/roots/bootstrap.rb
similarity index 52%
rename from menu/bootstrap.rb
rename to roots/bootstrap.rb
index 3ba60377..68ad0921 100644
--- a/menu/bootstrap.rb
+++ b/roots/bootstrap.rb
@@ -14,108 +14,122 @@ def self.menu_after *args
self.content *args[1..-1]
end
- MENU = %`
- <= .make a page/
+ # Called when MENU set, so subclasses can use it to make
+ # MENU have their name.
+ def self.menu_constant
+# <= .make a page/
+ txt = %`
- examples/
- layouts/
- hello world/
- - @#{self.to_s.downcase}/
- - project name/Sharkathon
- - h2/Sharks
- - p/They have fins and other cool stuff.
+ = bootstrap/
+ > A heading
+ You can just use a greater than sign for a heading.
+ - columns/
+ = bootstrap/
+ > Hi
+ Since these two sections are right next to each other, they'll show up side by side (as columns).
+ > Snails
+ Cool swirly shell but creepy antennae things.
- hero unit/
- - @#{self.to_s.downcase}/
- - project name/Sharkathon
+ = bootstrap/
- hero/
- - h1/Sharks
- - p/They have fins and other cool stuff.
-
- > Do you?
- Lorem Ipsum Dolor...
- - 2 columns/
- - @#{self.to_s.downcase}/
- - row/
- - span6/
- - h2/random
- - p/lorem ipsum...
- - span6/
- - h2/random
- - p/ipsum dolor...
- - 3 columns/
- - @#{self.to_s.downcase}/
- - row/
- - span4/
- - h2/random
- - p/lorem ipsum...
- - span4/
- - h2/random
- - p/ipsum dolor...
- - span4/
- - h2/random
- - p/dolor sit...
- - with icons/
- - @#{self.to_s.downcase}/
- - hero/
- - h1/Sharks
- - p/lorem...
- - row/
- - span6/
- - h2 icon/random
- - p/ipsum dolor sit...
- - span6/
- - h2 icon/random
- - p/dolor sit lorem...
- - row/
- - span4/
- - h2 icon/random
- - p/lorem ipsum...
- - span4/
- - h2 icon/random
- - p/ipsum dolor...
- - span4/
- - h2 icon/random
- - p/sit lorem...
- - shorthand/
- - @#{self.to_s.downcase}/
- - hero/
- > Shorthand
- p/Lines starting with ">" at left margin will have rows and spans auto-wrapped around them.
-
- > o random
- lorem...
- > o random
- lorem...
-
- > o random
- lorem...
- > o random
- lorem...
- > o random
- lorem...
- - with styled hero/
- - @#{self.to_s.downcase}/
- - hero/
- - h1/Sharks
- - p/lorem...
- - h2/random
- - p/sit lorem ipsum dolor...
- - style/
- | .hero-unit {
- | background-color: #9D261D;
- | color: #fff;
- | text-shadow: 2px 2px 2px #333;
- | border-radius: 20px;
- | }
- - modified navbar/
- - @#{self.to_s.downcase}/
- - navbar/
- |
- |
- - h2/random
- - p/sit lorem ipsum dolor...
+ h1/Sharks
+ p/They have fins and other cool stuff.
+ > Colors?
+ Each time you generate the page, it uses a different background color for the hero, just to give you ideas. Under "more control" there's an example of using a style to set it to a fixed color or gradient.
+ - rows/
+ = bootstrap/
+ > Snails
+ Cool swirly shell but creepy antennae things.
+ > random
+ This heading is random since we used the special heading "random".
+
+ > Another
+ This div is a underneath because of the blank line separating it. If you removed the blank line, it would be a third column.
+ - icons/
+ = bootstrap/
+ > $ Snails
+ The "$" is replaced with a random icon.
+ When you have "Lorem..." or "Ipsum Dolor..." or something like that, it uses sample text.
+ > $ Sample text
+ Lorem...
+ - html/
+ = bootstrap/
+ > You can put html pretty much anywhere
+
Here's me using some html.
+
+ - more control/
+ - 2 columns/
+ = bootstrap/
+ - project name/Sharks
+ - row/
+ - span5/
+ > random
+ lorem ipsum...
+ - span7/
+ > random
+ ipsum dolor...
+ - 3 columns/
+ = bootstrap/
+ - project name/Sharks
+ - row/
+ - span3/
+ > random
+ lorem ipsum...
+ - span6/
+ > random
+ ipsum dolor...
+ - span3/
+ > random
+ dolor sit...
+ - hero and icons/
+ = bootstrap/
+ - project name/Sharks
+ - hero/
+ h1/Sharks
+ p/lorem...
+ - row/
+ - span6/
+ h2 icon/random
+ ipsum dolor sit...
+ - span6/
+ h2 icon/random
+ dolor sit lorem...
+ - row/
+ - span4/
+ h2 icon/random
+ lorem ipsum...
+ - span4/
+ h2 icon/random
+ ipsum dolor...
+ - span4/
+ h2 icon/random
+ sit lorem...
+ - with styled hero/
+ = bootstrap/
+ - hero/
+ h1/Sharks
+ p/lorem...
+ - h2/random
+ - p/sit lorem ipsum dolor...
+ - style/
+ | .hero-unit {
+ | background-color: #9D261D;
+ | color: #fff;
+ | text-shadow: 2px 2px 2px #333;
+ | border-radius: 20px;
+ | }
+ - modified navbar/
+ = bootstrap/
+ navbar/
+ |
+ |
+ h2/random
+ p/sit lorem ipsum dolor...
- components/
- buttons/
- - @#{self.to_s.downcase}/
+ = bootstrap/
- h2/Buttons
- p/
Hi
@@ -133,7 +147,7 @@ def self.menu_after *args
Hi
- icons/
- with headings/
- - @#{self.to_s.downcase}/
+ = bootstrap/
p/
- h3/random
- h2 icon/Info
@@ -167,7 +181,7 @@ def self.menu_after *args
Music
- forms/
- basics/
- - @#{self.to_s.downcase}/
+ = bootstrap/
|
- search/
- - @#{self.to_s.downcase}/
+ = bootstrap/
|
- inline/
- - @#{self.to_s.downcase}/
+ = bootstrap/
|
- horizontal/
- - @#{self.to_s.downcase}/
+ = bootstrap/
|
- code/
- - @#{self.to_s.downcase}/
+ = bootstrap/
- pre/
| class Clam
| def hi
@@ -217,7 +231,7 @@ def self.menu_after *args
| end
- p/Hey
you
there.
- carousel/
- - @#{self.to_s.downcase}/
+ = bootstrap/
-
-
@@ -240,19 +254,32 @@ def self.menu_after *args
| Expand the 'container' menu to build, or start with the
| 'example' menu.
|
- | @#{self.to_s.downcase}/
+ | =bootstrap/
| h2/Example Heading
> Bootstrap site
- @http://twitter.github.com/bootstrap/
+ =http://twitter.github.com/bootstrap/
- javascript.html
- base-css.html
- getting-started.html#examples
- see/
- @fontawesome/
+ =fontawesome/
`
+ name = self.name.to_s.downcase.sub(/.+:/, '')
+ txt.gsub!("=bootstrap/", "=#{name}/") if name != "bootstrap"
+
+ txt
+
+ end
+
+ MENU = self.menu_constant
+
+ # @@bg = ['#eee', '#555', '#999', '#9D261D', '#369', '#096']
+
+
+ # Temp >>> Hard-coded > because tomato color is the best!
+ @@bg = ['#9D261D']
- @@bg = ['#eee', '#555', '#999', '#9D261D', '#369', '#096']
@@tags = {
"container"=>"div class='container'",
@@ -269,7 +296,7 @@ def self.menu_after *args
}
@@icons = [
- 'expand-alt', 'frown', 'meh', 'gamepad', 'keyboard', 'flag-alt', 'flag-checkered', 'mail-forward', 'mail-reply', 'reply-all', 'mail-reply-all', 'star-half-empty', 'star-half-full', 'location-arrow', 'rotate-left', 'rotate-right', 'crop', 'unlink', 'question', 'info', 'superscript', 'subscript', 'puzzle-piece', 'microphone', 'microphone-off', 'shield', 'calendar-empty', 'fire-extinguisher', 'rocket', 'maxcdn', 'html5', 'css3', 'anchor', 'unlock-alt', 'bullseye', 'rss-sign', 'play-sign', 'ticket', 'minus-sign-alt', 'check-minus', 'level-up', 'level-down', 'check-sign', 'edit-sign', 'external-link-sign', 'share-sign', 'adjust', 'asterisk', 'ban-circle', 'bar-chart', 'barcode', 'beaker', 'beer', 'bell-alt', 'bell', 'bolt', 'book', 'bookmark-empty', 'bookmark', 'briefcase', 'bullhorn', 'calendar', 'camera-retro', 'camera', 'certificate', 'check-empty', 'check', 'circle-blank', 'circle', 'cloud-download', 'cloud-upload', 'cloud', 'code-fork', 'code', 'coffee', 'cog', 'cogs', 'collapse-alt', 'comment-alt', 'comment', 'comments-alt', 'comments', 'credit-card', 'crop', 'dashboard', 'desktop', 'download-alt', 'download', 'edit', 'ellipsis-horizontal', 'ellipsis-vertical', 'envelope-alt', 'envelope', 'eraser', 'exchange', 'exclamation-sign', 'exclamation', 'external-link-sign', 'external-link', 'eye-close', 'eye-open', 'facetime-video', 'fighter-jet', 'film', 'filter', 'fire-extinguisher', 'fire', 'flag-alt', 'flag-checkered', 'flag', 'folder-close-alt', 'folder-close', 'folder-open-alt', 'folder-open', 'food', 'frown', 'gamepad', 'gift', 'glass', 'globe', 'group', 'hdd', 'headphones', 'heart-empty', 'heart', 'home', 'inbox', 'info-sign', 'info', 'key', 'keyboard', 'laptop', 'leaf', 'legal', 'lemon', 'level-down', 'level-up', 'lightbulb', 'location-arrow', 'lock', 'magic', 'magnet', 'mail-forward', 'mail-reply', 'mail-reply-all', 'map-marker', 'minus-sign-alt', 'minus-sign', 'minus', 'mobile-phone', 'money', 'move', 'music', 'off', 'ok-circle', 'ok-sign', 'ok', 'pencil', 'phone-sign', 'phone', 'picture', 'plane', 'plus-sign', 'plus', 'print', 'pushpin', 'puzzle-piece', 'qrcode', 'question-sign', 'question', 'quote-left', 'quote-right', 'random', 'refresh', 'remove-circle', 'remove-sign', 'remove', 'reorder', 'reply-all', 'reply', 'resize-horizontal', 'resize-vertical', 'retweet', 'road', 'rocket', 'rotate-left', 'rotate-right', 'rss-sign', 'rss', 'screenshot', 'search', 'share-alt', 'share-sign', 'share', 'shopping-cart', 'sign-blank', 'signal', 'signin', 'signout', 'sitemap', 'smile', 'sort-down', 'sort-up', 'sort', 'spinner', 'star-empty', 'star-half-full', 'star-half-empty', 'star-half', 'star', 'tablet', 'tag', 'tags', 'tasks', 'terminal', 'thumbs-down', 'thumbs-up', 'ticket', 'time', 'tint', 'trash', 'trophy', 'truck', 'umbrella', 'unlock-alt', 'unlock', 'upload-alt', 'upload', 'user-md', 'user', 'volume-down', 'volume-off', 'volume-up', 'warning-sign', 'wrench', 'zoom-in', 'zoom-out', 'file', 'file-alt', 'cut', 'copy', 'paste', 'save', 'undo', 'repeat', 'text-height', 'text-width', 'align-left', 'align-center', 'align-right', 'align-justify', 'indent-left', 'indent-right', 'font', 'bold', 'italic', 'strikethrough', 'underline', 'superscript', 'subscript', 'link', 'unlink', 'paper-clip', 'columns', 'table', 'th-large', 'th', 'th-list', 'list', 'list-ol', 'list-ul', 'list-alt', 'angle-left', 'angle-right', 'angle-up', 'angle-down', 'arrow-down', 'arrow-left', 'arrow-right', 'arrow-up', 'caret-down', 'caret-left', 'caret-right', 'caret-up', 'chevron-down', 'chevron-left', 'chevron-right', 'chevron-up', 'chevron-sign-left', 'chevron-sign-right', 'chevron-sign-up', 'chevron-sign-down', 'circle-arrow-down', 'circle-arrow-left', 'circle-arrow-right', 'circle-arrow-up', 'double-angle-left', 'double-angle-right', 'double-angle-up', 'double-angle-down', 'hand-down', 'hand-left', 'hand-right', 'hand-up', 'play-circle', 'play-sign', 'play', 'pause', 'stop', 'eject', 'backward', 'forward', 'fast-backward', 'fast-forward', 'step-backward', 'step-forward', 'fullscreen', 'resize-full', 'resize-small', 'facebook', 'facebook-sign', 'twitter', 'twitter-sign', 'github', 'github-sign', 'html5', 'linkedin', 'linkedin-sign', 'maxcdn', 'pinterest', 'pinterest-sign', 'google-plus', 'google-plus-sign', 'ambulance', 'h-sign', 'hospital', 'medkit', 'plus-sign-alt', 'stethoscope', 'user-md',
+ 'expand-alt', 'meh', 'keyboard', 'microphone', 'microphone-off', 'shield', 'calendar-empty', 'css3', 'anchor', 'bullseye', 'check-minus', 'check-sign', 'edit-sign', 'adjust', 'asterisk', 'ban-circle', 'bar-chart', 'barcode', 'beaker', 'beer', 'bell-alt', 'bell', 'bolt', 'book', 'bookmark-empty', 'bookmark', 'briefcase', 'bullhorn', 'calendar', 'camera-retro', 'camera', 'certificate', 'check-empty', 'check', 'circle-blank', 'circle', 'cloud-download', 'cloud-upload', 'cloud', 'code-fork', 'code', 'coffee', 'cog', 'cogs', 'collapse-alt', 'comment-alt', 'comment', 'comments-alt', 'comments', 'credit-card', 'crop', 'dashboard', 'desktop', 'download-alt', 'download', 'edit', 'ellipsis-horizontal', 'ellipsis-vertical', 'envelope-alt', 'envelope', 'eraser', 'exchange', 'exclamation-sign', 'exclamation', 'external-link-sign', 'external-link', 'eye-close', 'eye-open', 'facetime-video', 'fighter-jet', 'film', 'filter', 'fire-extinguisher', 'fire', 'flag-alt', 'flag-checkered', 'flag', 'folder-close-alt', 'folder-close', 'folder-open-alt', 'folder-open', 'food', 'frown', 'gamepad', 'gift', 'glass', 'globe', 'group', 'hdd', 'headphones', 'heart-empty', 'heart', 'home', 'inbox', 'info-sign', 'info', 'key', 'laptop', 'leaf', 'legal', 'lemon', 'level-down', 'level-up', 'lightbulb', 'location-arrow', 'lock', 'magic', 'magnet', 'mail-forward', 'mail-reply', 'mail-reply-all', 'map-marker', 'minus-sign-alt', 'minus-sign', 'minus', 'mobile-phone', 'money', 'move', 'music', 'off', 'ok-circle', 'ok-sign', 'ok', 'pencil', 'phone-sign', 'phone', 'picture', 'plane', 'plus-sign', 'plus', 'print', 'pushpin', 'puzzle-piece', 'qrcode', 'question-sign', 'question', 'quote-left', 'quote-right', 'random', 'refresh', 'remove-circle', 'remove-sign', 'remove', 'reorder', 'reply-all', 'reply', 'resize-horizontal', 'resize-vertical', 'retweet', 'road', 'rocket', 'rotate-left', 'rotate-right', 'rss-sign', 'rss', 'screenshot', 'search', 'share-alt', 'share-sign', 'share', 'shopping-cart', 'sign-blank', 'signal', 'signin', 'signout', 'sitemap', 'smile', 'sort-down', 'sort-up', 'sort', 'spinner', 'star-empty', 'star-half-full', 'star-half-empty', 'star-half', 'star', 'tablet', 'tag', 'tags', 'tasks', 'terminal', 'thumbs-down', 'thumbs-up', 'ticket', 'time', 'tint', 'trash', 'trophy', 'truck', 'umbrella', 'unlock-alt', 'unlock', 'upload-alt', 'upload', 'user', 'volume-down', 'volume-off', 'volume-up', 'warning-sign', 'wrench', 'zoom-in', 'zoom-out', 'file', 'file-alt', 'cut', 'copy', 'paste', 'save', 'undo', 'repeat', 'text-height', 'text-width', 'align-left', 'align-center', 'align-right', 'align-justify', 'indent-left', 'indent-right', 'font', 'bold', 'italic', 'strikethrough', 'underline', 'superscript', 'subscript', 'link', 'unlink', 'paper-clip', 'columns', 'table', 'th-large', 'th', 'th-list', 'list', 'list-ol', 'list-ul', 'list-alt', 'angle-left', 'angle-right', 'angle-up', 'angle-down', 'arrow-down', 'arrow-left', 'arrow-right', 'arrow-up', 'caret-down', 'caret-left', 'caret-right', 'caret-up', 'chevron-down', 'chevron-left', 'chevron-right', 'chevron-up', 'chevron-sign-left', 'chevron-sign-right', 'chevron-sign-up', 'chevron-sign-down', 'circle-arrow-down', 'circle-arrow-left', 'circle-arrow-right', 'circle-arrow-up', 'double-angle-left', 'double-angle-right', 'double-angle-up', 'double-angle-down', 'hand-down', 'hand-left', 'hand-right', 'hand-up', 'play-circle', 'play-sign', 'play', 'pause', 'stop', 'eject', 'backward', 'forward', 'fast-backward', 'fast-forward', 'step-backward', 'step-forward', 'fullscreen', 'resize-full', 'resize-small', 'facebook', 'facebook-sign', 'twitter', 'twitter-sign', 'github', 'github-sign', 'html5', 'linkedin', 'linkedin-sign', 'maxcdn', 'pinterest', 'pinterest-sign', 'google-plus', 'google-plus-sign', 'ambulance', 'h-sign', 'hospital', 'medkit', 'plus-sign-alt', 'stethoscope', 'user-md',
]
@@random = ["Info", "Facts", "B. S.", "Why?", "History", "Traits", "Story", "What?", "Happy"]
@@ -278,8 +305,11 @@ def self.menu_after *args
# Render txt as html.
# Bootstrap.render "h1/foo"
def self.render txt
+
+ txt.gsub! /^ *\|-.*\n/, '' # Delete |-... "comments"
+
bootstrap = Bootstrap.new txt
- bootstrap.to_html
+ bootstrap.to_html #> |||
bootstrap.wrap_html_page
end
@@ -290,36 +320,121 @@ def self.make_a_page
# If more siblings
"
- - project name/Foo
- hero/
- row/
- icon/
- style/
+ - project name/
"
end
+ def self.simplified_render txt
+
+ snippet = txt.unindent
+
+ html = %`
+
+
+
+
+
+
+
+
+
+
+
+
Bootstrap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#{snippet}
+
+
+
+
+
+
+
+
+
+
+ `
+
+
+ return Browser.html html
+
+
+
+ end
+
+
def self.content *args
prefix = Keys.prefix :clear=>1
# If not expandable or open+, render in browser
if prefix == "open" || Line !~ /\/$/ || Line =~ /^ *\|/
- orig = Location.new
+ return self.simplified_render args[0]
- Tree.to_root
+ Ol "Todo > clean up unused stuff below here!"
- txt = Tree.children :cross_blank_lines=>1
- orig.go
+ txt = args[0]
txt = txt.unindent
+ txt = "
#{txt}
"
- txt = self.render txt
-
+ txt = self.render txt #> ||
return Browser.html txt
end
last = args.last
if filler = @@filler[last]
- Line.add_slash :txt=>filler, :left=>1
+ Line.add_slash :txt=>filler, :no_move=>1
elsif last == "icon"
@@icons.map{|o| "- #{o}\n"}.join('')
elsif last == 'style'
@@ -363,7 +478,9 @@ def initialize txt
def self.expand_wiki_headings txt
# Always simply wrap >... lines that are indented...
- txt.gsub! /( +)> /, "\\1h2/"
+ txt.gsub! /^( *hero\/\n *)> /, "\\1h1/" # If under "hero/" make h1
+
+ txt.gsub! /^( +)> /, "\\1h2/"
return if txt !~ /^> / # If no >... lines at left margin, we're done (because only they have are meant to be wrapped)
@@ -395,12 +512,12 @@ def self.expand_wiki_headings txt
no_h2_yet = true
row_i = 0
lines.each do |l|
- if l =~ /^> (\w )?(.+)/ # If >..., increase count in hash
+ if l =~ /^> (\$ )?(.+)/ # If >..., increase count in hash
icon, heading = $1, $2
width = 12 / row_hash[row_i]
txt << "row/\n" if no_h2_yet # If first unindented >..., add initial row
no_h2_yet = false
- next txt.<< " span#{width}/\n h2 #{icon.strip}/#{heading}\n" if icon
+ next txt.<< " span#{width}/\n h2 icon/#{heading}\n" if icon
txt << " span#{width}/\n h2/#{heading}\n"
elsif no_h2_yet # Leave items before first >... alone
txt << "#{l}\n"
@@ -426,8 +543,6 @@ def to_html
# Remove |#... comments
@txt.gsub! /^ *\|# .+\n/, ''
- Bootstrap.expand_wiki_headings @txt
-
# Wrap "- container/" around all, unless already a container
if @txt !~ /^ *([+-] )?container\// && @txt !~ /^ *([+-] )?
tags.
- @html.gsub!(//) { |o| " #{Bootstrap.icon_tag @@icons[rand(93)]}" }
+ @html.gsub!("") { |o| " #{Bootstrap.icon_tag @@icons[rand(93)]}" }
# Make all lines within tags become icons.
@@ -496,7 +611,7 @@ def to_html
end
def self.icon_tag name
- " "
+ " "
end
def wrap_html_page
@@ -517,20 +632,29 @@ def wrap_html_page
padding-top: 60px;
padding-bottom: 40px;
}
+
-
-
+
+
+
+
+
+
#{@scripts}
-
`.gsub(/^ /, '')
diff --git a/roots/brew.menu b/roots/brew.menu
new file mode 100644
index 00000000..339b7185
--- /dev/null
+++ b/roots/brew.menu
@@ -0,0 +1,18 @@
+- before each use/
+ =% brew update
+ - refetch if problems/
+ =/usr/local/
+ % git fetch origin
+ % git reset --hard origin/master
+ - doctor, to detect problems/
+ =% brew doctor
+- install/
+ =% brew install coffeescript
+- list/
+ ! Tree.quote Shell.sync "brew list"
+- setup/
+ - make sure things are ok/
+ =% brew doctor
+- files/
+ =/usr/local/Cellar/
+=notes/
diff --git a/roots/browser/default.conf b/roots/browser/default.conf
new file mode 100644
index 00000000..e89f5488
--- /dev/null
+++ b/roots/browser/default.conf
@@ -0,0 +1,2 @@
+| Web browser to use. Optionally change "firefox" to "chrome".
+browser: firefox
diff --git a/roots/browser/menu.menu b/roots/browser/menu.menu
new file mode 100644
index 00000000..2465b76b
--- /dev/null
+++ b/roots/browser/menu.menu
@@ -0,0 +1,23 @@
+- url/
+ ! return " pass a url here" if args.blank?
+ ! Browser.url args.join('/')
+- reload/
+ ! Browser.reload
+- tabs/
+ ! Browser.tabs *args
+- source/
+ ! Tree.quote Browser.source(*args)
+- see/
+ <= web development/
+ <= firefox/
+- api/
+ | This class lets you choose a default browser.
+ |
+ | Calls to Browser.url etc. will be delegated to the default browser.
+- docs/
+ > Keys
+ | do+load+browser: Reload the browser.
+ |
+ > See
+ | Many things have yet to be pulled out of firefox.rb and made generic.
+ << firefox/
diff --git a/roots/bullets.menu b/roots/bullets.menu
new file mode 100644
index 00000000..6bb71cf6
--- /dev/null
+++ b/roots/bullets.menu
@@ -0,0 +1,112 @@
+=~/Dropbox/xiki/lib/xiki/core/
+ - launcher.rb
+ - based on bullets in expanded line:
+ : def self.process_target_bullets_before line
+ : # << foo, so replace parent before expanding...
+ : # <= foo, so replace all parents up to "=" before expanding...
+ : def self.process_target_bullets_after line, txt, insert_options # , path, options
+ : # <+ foo, so delete current line before inserting...
+ : # <: foo, so delete siblings before inserting...
+ - based on bullets in output:
+ : def self.process_output_bullets txt, options={}, insert_options={} # Based on bullets or =replace
+ : # <* or =flash/, so flash message...
+ : # or =prompt/...
+ : # <: and <+ replace siblings or line...
+ : # <<< foo/, so replace item with output and launch...
+old/
+ overview/
+ : Several <... bullet syntaxes exist that make items have
+ : special handling when you expand them. The most common one
+ : is "<< item/", which moves "item" up in the tree one level
+ : so that it replaces its parent, then expands it.
+ :
+ : Some bullets are meant to be visible in the tree before
+ : they're expanded. Others are meant to be returned as part
+ : of the output and immediately affect how/where it is
+ : displayed, and thus are not shown.
+ examples/
+ simple/
+ : <* flash me
+ | Temporarily insert and flash the message.
+ example/
+ =/tmp/
+ - foo.menu
+ : <* flash me
+ foo//
+ : prompt this
+ | Move to end of item, then temporarily insert and flash the
+ | message.
+ example/
+ =/tmp/
+ - foo.menu
+ : prompt this
+ foo//
+ move before expand/
+ | These bullets make the item move (like to replace its parent). After
+ | the move, the item will be expanded. Drill in to see descriptions
+ | and examples:
+ : <
+ |+probably don't have this in the menu, it's related to routes
+ | when in route, it defines it as if at the root (todo)
+ : <<
+ | Replace parent, then expand
+ example/
+ =/tmp/
+ - foo.menu
+ : a/
+ : b/
+ : << c/
+ - foo//
+ a/
+ c/
+ : <<<
+ | consider: already means to replace parent and launch, when on same line
+ | probably: still make <<< behave differently when it's the only line in the output?
+ | - might be confusing
+ | Replace grandparent, then expand
+ : <@
+ | Replace parent, but don't expand
+ : <:
+ : <=
+ |+Make this assign instead?
+ : - what's the new one for replacing the root menu?
+ : - <: ?
+ |-Replace root menu, then expand
+ - example/
+ : old?
+ =network stuff/
+ <= ip/
+ - move after expand/
+ | These expand the item first, then move the output according to the
+ | bullet. Drill in to see descriptions and examples.
+ : <+
+ | __Expand the item, then output replaces it
+ : <~
+ | __Expand the item, then output replaces it and its siblings
+ - when bullets in output/
+ : <$
+ | Moves output up to $... line and replaces it
+ future ones to do, maybe/
+ : <<-
+ : Replace value?
+ - as though at root, in place/
+ : Symbol to run as though it were at the root?!:
+ =face/
+ + hair/
+ + whatevs/
+ <* hair/
+ <: hair/
+ << hi/
+ - old/
+ - <-/
+ : Replace value?
+ - words
+ | Mention the words that you can return also?
+ =flash/flash me
+ =prompt/flash me at end
+ see/
+ | Also, output of menus can have bullets in a special syntax,
+ | which makes the output replace something immediately, rather
+ | than just being inserted under. See:
+ <= replace/
+ <= beg/
diff --git a/menu/calendar.rb b/roots/calendar.rb
similarity index 69%
rename from menu/calendar.rb
rename to roots/calendar.rb
index a933a2a2..97bbffb3 100644
--- a/menu/calendar.rb
+++ b/roots/calendar.rb
@@ -1,14 +1,23 @@
class Calendar
+
def self.menu month=nil
- Xiki.dont_search
- return "jan/\nfeb/\nmar/\napr/\nmay/\njun/\njul/\naug/\nsep/\noct/\nnov/\ndec/" if month == "year"
+ options = yield
+ task = options[:task]
+
+ # Handle tasks...
+
+ return "* year" if options[:task] == []
+
+ if options[:task] == ["year"]
+ return "- jan/\n- feb/\n- mar/\n- apr/\n- may/\n- jun/\n- jul/\n- aug/\n- sep/\n- oct/\n- nov/\n- dec/"
+ end
now = Time.now
no_month = month.nil? # Remember whether there was no month
month ||= now.month # If no month, assume current
txt = self.month month, now.year
- txt = "#{now.strftime("%b").downcase}/\n#{txt.gsub /^/, ' '}\n<= year/" if no_month # Add more options
+ txt = "^ options\n- #{now.strftime("%b").downcase}/\n#{txt.gsub /^/, ' '}" if no_month # Add more options
txt
end
diff --git a/roots/chmod.rb b/roots/chmod.rb
new file mode 100644
index 00000000..b5bc7a78
--- /dev/null
+++ b/roots/chmod.rb
@@ -0,0 +1,34 @@
+module Xiki
+ class Chmod
+ def self.menu mode=nil
+
+ path = Tree.closest_dir yield[:ancestors]
+
+ return "| Nest this menu under a file, like...\n|\n| /tmp/\n| @chmod/" if ! path
+
+ if ! mode
+ base_10_number = File.stat(path).mode rescue 33188
+Ol "base_10_number", base_10_number
+ mode = sprintf("%o", base_10_number)[/...$/]
+ return "
+ - executable) 755/
+ - normal) 644/
+ - only owner) 600/
+ - unrestricted) 777/
+ - original) #{mode}/
+ "
+ end
+
+ # Arg passed, so do chmod
+
+ command = "chmod #{mode} \"#{path}\""
+ if ! File.writable? path
+ command = "sudo #{command}"
+ Shell.run command, :dir=>path
+ return
+ end
+
+ Shell.sync command, :dir=>path
+ end
+ end
+end
diff --git a/lib/xiki/tools/chown.rb b/roots/chown.rb
similarity index 91%
rename from lib/xiki/tools/chown.rb
rename to roots/chown.rb
index 2d56e4f8..bec7d2cf 100644
--- a/lib/xiki/tools/chown.rb
+++ b/roots/chown.rb
@@ -15,7 +15,7 @@ def self.menu user_group=nil
return "
- original) #{user_group}/
- > Suggestions:
+ > Suggestions
- #{default_user}:#{default_group}/
- root:wheel/
"
@@ -26,7 +26,7 @@ def self.menu user_group=nil
command = "chown #{user_group} \"#{path}\""
command = "sudo #{command}" # Trickier with user, so just always sudo for now
- Console.run command, :dir=>path
+ Shell.run command, :dir=>path
end
diff --git a/roots/chrome.rb b/roots/chrome.rb
new file mode 100644
index 00000000..7602b65f
--- /dev/null
+++ b/roots/chrome.rb
@@ -0,0 +1,83 @@
+class Chrome
+
+ def self.reload
+ Xiki::Applescript.run %`
+ tell application "Google Chrome"
+ reload active tab of front window
+ end tell
+ `.unindent
+ end
+
+ def self.html txt, options={}
+
+ file = options[:file] || "/tmp/chrome.html"
+
+ File.open(file, "w") { |f| f << txt }
+
+ Xiki::Applescript.run %`
+ tell application "Google Chrome"
+ set URL of active tab of front window to "file://#{file}"
+ end tell
+ `.unindent
+
+
+ # result = Xiki::Applescript.run txt
+
+ end
+
+ def self.js txt
+
+ Xiki::Tree.unquote! txt
+ txt.gsub! '"', '\"'
+ txt.gsub! "\n", "\\n"
+
+ txt = %`
+ tell application "Google Chrome"
+ tell window 1
+ tell active tab
+ execute javascript "#{txt}"
+ end tell
+ end tell
+ end tell
+ `
+
+ result = Xiki::Applescript.run txt
+
+ nil
+
+ end
+
+ def self.menu *args
+Ol()
+ return "> Type some js to run in chrome\n: alert('hi')\n" if args.blank?
+
+ txt = args[0]
+
+ # Install clojure via leiningen
+ | How to install on the mac:
+ @% brew install leiningen
+ =conf/
+ `
+
+ def self.remote path=nil
+
+ # /remote/, so list them...
+
+ conf = yield[:conf]
+ conf.sub(/\A> .+\n/, '').gsub(/.+/, "- \\0/")
+
+ # /remote/foo.com, so connect or start...
+
+ "TODO: not implemented yet. Here's the path: #{path}"
+ end
+
+ def self.repl
+ Shell.run "lein repl"
+ ""
+ end
+
+ def self.menu_before *args
+
+ return if args.blank? # Do nothing if just "clojure/"
+
+ # If clojure jar not set, error out...
+
+ conf = yield[:conf]
+ return "
+ > Expand this and add your clojure jar here first
+ =clojure/=conf/
+ |
+ | Hint, it's probably somewhere under here:
+ @~/.m2/repository/org/clojure/clojure/
+ " if conf !~ /\.jar/
+ end
+
+ def self.menu_after output, *args
+ return if args[0] !~ /\n/ # Only continue if :... lines passed in
+
+ self.run args[0], yield # Eval them
+ end
+
+ def self.run txt, options
+
+ file = "/tmp/tmp.clj"
+ File.open(file, "w") { |f| f << txt }
+
+ conf = options[:conf]
+ clojure_jar = conf[/.+\.jar$/]
+
+ command = "java -cp #{clojure_jar} clojure.main -i #{file}"
+ output = Shell.sync command
+
+ Tree.quote output
+ end
+
+ end
+end
diff --git a/roots/clojure/default.conf b/roots/clojure/default.conf
new file mode 100644
index 00000000..b542feb9
--- /dev/null
+++ b/roots/clojure/default.conf
@@ -0,0 +1,5 @@
+> Clojure jar
+
+
+> Put your remote repl paths here
+foo.com:8001
diff --git a/roots/close.rb b/roots/close.rb
new file mode 100644
index 00000000..55cdfca2
--- /dev/null
+++ b/roots/close.rb
@@ -0,0 +1 @@
+View.kill
diff --git a/roots/close_view.rb b/roots/close_view.rb
new file mode 100644
index 00000000..55cdfca2
--- /dev/null
+++ b/roots/close_view.rb
@@ -0,0 +1 @@
+View.kill
diff --git a/roots/code.rb b/roots/code.rb
new file mode 100644
index 00000000..6e700937
--- /dev/null
+++ b/roots/code.rb
@@ -0,0 +1 @@
+Xiki["ruby/", options]
diff --git a/roots/coffee.rb b/roots/coffee.rb
new file mode 100644
index 00000000..817d47a6
--- /dev/null
+++ b/roots/coffee.rb
@@ -0,0 +1,25 @@
+# Handle tasks
+
+return "* compile" if task == []
+if task == ["compile"]
+ return Tree.quote Shell.command "coffee -c --stdio", :stdin=>args[0]
+end
+
+# /, so show message...
+
+return "
+ | # Type some coffeescript here
+ | console.log 'hi'
+ " if args == []
+
+# /code, so execute it...
+
+# If coffeescript not installed, give them "gem install" command...
+
+return "
+ > First install coffeescript
+ % gem install coffeescript
+ " if `which coffee` == ""
+
+Tree.quote CoffeeHandler.eval args[0]
+
diff --git a/roots/computer.menu b/roots/computer.menu
new file mode 100644
index 00000000..fc63ec35
--- /dev/null
+++ b/roots/computer.menu
@@ -0,0 +1,5 @@
+= ip/
+= disk/
+= cpu/
+= processes/
+= network/
diff --git a/menu/conf/index.rb b/roots/conf/conf_index.rb
similarity index 50%
rename from menu/conf/index.rb
rename to roots/conf/conf_index.rb
index 14483882..aa62de68 100644
--- a/menu/conf/index.rb
+++ b/roots/conf/conf_index.rb
@@ -3,18 +3,12 @@ class Conf
include Xiki
- MENU = "
- > options
- - docs/
- > Whatevs
- "
-
# Can be called 2 ways, nested or not:
# conf/
# - foo/
# | content
# foo/
- # @conf/
+ # =conf/
# | content
# - docs/
def self.menu_after output=nil, *args # arg1=nil, arg2=nil
@@ -22,22 +16,30 @@ def self.menu_after output=nil, *args # arg1=nil, arg2=nil
return if output && args.any? # MENU handled everything, if there was a path and autput
options = yield
- # Use parent as menu name if foo/@conf...
+ # Use ancestor (or ancestor) as menu name if foo/=conf...
+
+ if ancestors = options[:ancestors]
+ menuish_ancestor = ancestors[-1][/[\w ]+/]
+ end
- menuish_parent = Xiki.menuish_parent options
+ # menuish_ancestor = Xiki.menuish_parent options
- # Don't use parent if there's a foo/@conf/bar item that overrides
- args.unshift menuish_parent if menuish_parent && (args.blank? || args[0] =~ /^\|/)
+ # Don't use ancestor it's a herring/=conf/legit situation (if child is item-ish, use it)
+ args.unshift menuish_ancestor if menuish_ancestor && (args.blank? || args[0] =~ /\n/)
name, content = args
# /..., so list all conf-able menus...
return self.menus(output) if ! name
- # /foo/... or /foo/|content...
+ # /foo/... or /foo/|content, so show conf or save...
- # TODO: remove Tree.txt editor dependency
- ConfHandler.txt name, content ? Tree.txt : nil
+ options[:no_search] = 1
+
+ item = args[-1]
+
+ # Content seems sufficient
+ ConfHandler.txt name, content, item, options
end
@@ -45,20 +47,18 @@ def self.menus output
menus = []
- Xiki.menu_path_dirs.each do |path_dir|
+ Xiki.xiki_path_dirs.each do |path_dir|
Dir["#{path_dir}/*"].each do |menu_dir|
- #Ol "menu_dir", menu_dir
conf_path = "#{menu_dir}/default.conf"
next unless File.exists? conf_path
- menus << "- #{File.basename menu_dir}/"
+ menus << "+ #{File.basename menu_dir}/"
end
end
- Dir["#{Xiki.menu_path_custom_dir}/conf/*.conf"].each do |conf_file|
- menus << "- #{File.basename(conf_file, ".*")}/"
-
+ Dir["#{Xiki.xiki_path_custom_dir}/conf/*.conf"].each do |conf_file|
+ menus << "+ #{File.basename(conf_file, ".*")}/"
end
menus.sort!
diff --git a/roots/copy.rb b/roots/copy.rb
new file mode 100644
index 00000000..5cb897ca
--- /dev/null
+++ b/roots/copy.rb
@@ -0,0 +1 @@
+Xiki["`rename/", options.merge(:copy=>1)]
diff --git a/lib/xiki/tools/couch.rb b/roots/couch.rb
similarity index 73%
rename from lib/xiki/tools/couch.rb
rename to roots/couch.rb
index ab14e307..cfc7d124 100644
--- a/lib/xiki/tools/couch.rb
+++ b/roots/couch.rb
@@ -1,26 +1,28 @@
gem 'couchrest'
require 'couchrest'
+require 'httparty'
module Xiki
class Couch
- @@server = 'http://localhost:5984'
+ @@server = 'http://memorize.loc:5984'
- def self.menu
- "
+ MENU = "
- .start/
- - @db/
- - .admin url/
+ - =db/
+ - .web admin/
+ - whether running/
+ =kill/couchdb/
+ =notes/
"
- end
def self.start
buffer = '*couchdb'
- return ".flash - *couchdb already open!" if View.buffer_open? buffer
+ return "<* *couchdb already open!" if View.buffer_open? buffer
- Console.run('sudo couchdb', :buffer=>buffer)
+ Shell.run('sudo couchdb', :buffer=>buffer)
end
- def self.admin_url
+ def self.web_admin
View.url "#{@@server}/_utils"
nil
end
@@ -69,35 +71,45 @@ def self.delete db, id=nil
# If no id, show all id's
if id.nil?
- all = RestTree.request 'GET', "#{@@server}/#{db}_all_docs", nil
+ all = HTTParty.get "#{@@server}/#{db}_all_docs"
rows = JSON[all]['rows']
return rows.map{|i| "#{i['id']}/"}
end
- self.escape_slashes id
+ self.escape_slashes! id
# If id, look it up to get rev
- record = RestTree.request 'GET', "#{@@server}/#{db}#{id}", nil
+ record = HTTParty.get "#{@@server}/#{db}#{id}"
rev = JSON[record]['_rev']
# Delete it
RestTree.request 'DELETE', "#{@@server}/#{db}#{id}?rev=#{rev}", nil
end
- def self.escape_slashes id
- # If id has multiple slashes, escape all but the last
- if id =~ /\/.+\/$/
- id.sub! /\/$/, '' # Remove last slash
- id.gsub!('/', '%2F') unless id =~ /^_design/ # Escape slashes
- id.sub! /$/, '/' # Put last back
- end
+ def self.escape_slashes! key
+
+ return if key =~ /^_design/ # Escape slashes
+
+ # Escape all slashes
+
+ key.gsub!('/', '%2F') unless
+
+ # Unescape the last
+
+ key.sub!(/%2F$/, '/')
end
- def self.docs db, id=nil, doc=nil
+ def self.docs db, *args
+ doc = args.pop if args[-1] =~ /\n/
+ key = args.join("/")
+ key = nil if key == ""
+
db.sub! /\/$/, ''
- # If no id, show all id's
- if id.nil?
- all = RestTree.request 'GET', "#{@@server}/#{db}/_all_docs", nil
+ # /db/, so show all key's...
+
+ if key.nil?
+
+ all = HTTParty.get "#{@@server}/#{db}/_all_docs"
if all =~ /no_db_file/
return "| DB not found, create it?\n- .create/"
@@ -114,25 +126,22 @@ def self.docs db, id=nil, doc=nil
return rows.map{|i| "#{i['id']}/"}
end
- self.escape_slashes id
+ self.escape_slashes! key
- # If just id, show doc
+ # /db/key/, so show doc...
if doc.nil?
-
- record = RestTree.request 'GET', "#{@@server}/#{db}/#{id}", nil
+ record = HTTParty.get "#{@@server}/#{db}/#{key}"
record = JSON[record]
record = record.to_yaml
record.sub! /.+\n/, ''
return record.gsub("\\n", "\n").gsub(/^/, '| ').gsub(/^\| $/, '|')
end
- # Doc passed, so save it
-
- doc = ENV['txt'].dup
+ # /db/key/doc, so save it...
# If a record is found, add rev
- record = RestTree.request 'GET', "#{@@server}/#{db}/#{id}", nil
+ record = HTTParty.get "#{@@server}/#{db}/#{key}"
if record !~ /404 /
rev = JSON[record]['_rev']
@@ -147,8 +156,8 @@ def self.docs db, id=nil, doc=nil
doc = YAML::load doc
doc = doc.to_json
# Update it
- res = RestTree.request 'PUT', "#{@@server}/#{db}/#{id}", doc
- ".flash - Updated!"
+ res = HTTParty.put "#{@@server}/#{db}/#{key}", :body=>doc
+ "<* updated!"
end
def self.views db
@@ -169,7 +178,7 @@ def self.all_docs db
def self.create db
RestTree.request 'PUT', "#{@@server}/#{db}", nil
- ".flash - created!"
+ "<* created!"
end
def self.crud db
diff --git a/roots/cpu.menu b/roots/cpu.menu
new file mode 100644
index 00000000..0e627a3c
--- /dev/null
+++ b/roots/cpu.menu
@@ -0,0 +1,2 @@
+- processes taking up cpu/
+ ! Tree.quote `ps -eo pcpu,pid,user,args | sort -k 1 -r | head -10`
diff --git a/menu/create_menus_manually.menu b/roots/create_menus_manually.menu
similarity index 78%
rename from menu/create_menus_manually.menu
rename to roots/create_menus_manually.menu
index d2f00287..0a5b3ece 100644
--- a/menu/create_menus_manually.menu
+++ b/roots/create_menus_manually.menu
@@ -1,26 +1,26 @@
> Summary
-| You can creating menus using any text editor, and use them via the web interface.
+| You can create menus using any text editor, and use them via the web interface.
| Later, after you set up a Xiki-enabled text editor, you'll be able to use the menus directly from the editor.
> Simplest way to make a menu
- txt file/
> Make a simple menu that is a .txt file
- 1. Using any text editor, make a new file and type "Hello World" as its contents.
- - 2. Save it with the name "foo.txt" in the "menu" directory in your home directory.
- - Create the "menu" directory if there isn't one.
+ - 2. Save it with the name "foo.txt" in a "menu" directory in your home directory.
+ - Create a "menu" directory if there isn't one.
- Be sure to save it as plain text (most editors do this by default).
- - 3. Go to http://localhost:8161/foo or click this...
+ - 3. Go to http://localhost:8163/foo or click this...
@foo/
| You'll see that the menu simply shows "Hello World".
-
> More ways to make menus
- ruby file/
> Make a simple menu that is a .rb file
- 1. Using any text editor, make a new file and type "2 + 3" as its contents.
- - 2. Save it with the name "bar.rb" in the "menu" directory in your home directory.
- - 3. Go to http://localhost:8161/bar or click this...
+ - Create a "menu" directory if there isn't one.
+ - 2. Save it with the name "bar.rb" in a "menu" directory in your home directory.
+ - 3. Go to http://localhost:8163/bar or click this...
@bar/
| You'll see that the menu simply shows "5".
@@ -28,7 +28,7 @@
> Make a simple menu that is a .menu file
- 1. Using any text editor, make a new file and type the following as its contents.
| snacks/ chips pretzels dessert/ cake
- - 2. Save it with the name "food.menu" in the "menu" directory in your home directory.
+ - 2. Save it with the name "food.menu" in a "menu" directory in your home directory.
- 3. Go to http://localhost:8161/food or click this...
@food/
@@ -37,7 +37,7 @@
> Make a simple menu with some visual elements
- 1. Using any text editor, make a new file and type the following as its contents.
| > Heading A paragraph of text. a menu item/ > Heading under the item A paragraph under the item.
- - 2. Save it with the name "baz.menu" in the "menu" directory in your home directory.
+ - 2. Save it with the name "baz.menu" in a "menu" directory in your home directory.
- 3. Go to http://localhost:8161/baz or click this...
@baz/
@@ -46,7 +46,7 @@
> Make a simple menu with items that run code
- 1. Using any text editor, make a new file and type the following as its contents.
| wind/ ! "howl" * 2 rain/ ! "drop" * 3
- - 2. Save it with the name "weather.menu" in the "menu" directory in your home directory.
+ - 2. Save it with the name "weather.menu" in a "menu" directory in your home directory.
- 3. Go to http://localhost:8161/weather or click this...
@weather/
@@ -55,7 +55,7 @@
> Make a simple menu with items that run code
- 1. Using any text editor, make a new file and type the following as its contents.
| class Hello def hi "hi there" end def later "adios" end end
- - 2. Save it with the name "hello.rb" in the "menu" directory in your home directory.
+ - 2. Save it with the name "hello.rb" in a "menu" directory in your home directory.
- 3. Go to http://localhost:8161/hello or click this...
@hello/
@@ -64,7 +64,7 @@
> Make a simple menu with items that run code
- 1. Using any text editor, make a new file and type the following as its contents.
| > Heading A sentence. > Another More Words.
- - 2. Save it with the name "hey.bootstrap" in the "menu" directory in your home directory.
+ - 2. Save it with the name "hey.bootstrap" in a "menu" directory in your home directory.
- 3. Go to http://localhost:8161/hey or click this...
@hey/
diff --git a/roots/cs.rb b/roots/cs.rb
new file mode 100644
index 00000000..9dfe80f0
--- /dev/null
+++ b/roots/cs.rb
@@ -0,0 +1,14 @@
+return "
+ | # Type some coffeescript here, to run in the browser
+ | alert 'hi'
+
+ > See
+ << coffee/
+ " if args.empty?
+
+return "=beg/quoted/" if args[0] !~ /\n/
+
+txt = CoffeeScript.to_js args[0]
+Browser.js txt
+
+"<* ran in browser!"
diff --git a/lib/xiki/tools/css.rb b/roots/css.rb
similarity index 73%
rename from lib/xiki/tools/css.rb
rename to roots/css.rb
index 6462bf87..1ec524b7 100644
--- a/lib/xiki/tools/css.rb
+++ b/roots/css.rb
@@ -1,3 +1,5 @@
+require "#{Xiki.dir}roots/dom"
+
module Xiki
class Css
def self.menu_before *args
@@ -6,13 +8,13 @@ def self.menu_before *args
return self.list args[1..-1] if args[0] == "list"
+ # Make this start with dot now?
# If css/foo, treat as style
- if args[0] && args[0] =~ /^[a-z]/i
+ if args[0] && args[0] =~ /^[a-z]/i && args[0] !~ /\n/
return self.list args
end
-
nil
end
@@ -30,17 +32,17 @@ def self.list args
result;
`.unindent
- result = Firefox.exec js
+ result = Browser.js js
# When dups, make foo,foo be foo,foo:2, etc.
result = self.add_nth_to_dups result
- return "> No class attributes found!" if result == '""'
- return result
+ return "> No class attributes found in page!" if result == '""'
+ return result.gsub /^\+ /, '+ .'
end
# Add "." to beginning of first (it was removed due to it meaning a method call)
- args.first.sub! /^/, '.'
+ # args.first.sub! /^/, '.'
Dom.dom *args
end
@@ -55,29 +57,35 @@ def self.menu *args
| font-family: arial;
| font-size: 15px;
| color: #333;
- | background-color: #fff;
+ | background-color: #eee;
| margin: 30px;
| }
- + list/
"
end
- txt = ENV['txt'].dup
+ txt = args[0]
+ return "=beg/quoted/" if txt !~ /\n/
+
+ # =extract
+
+ self.send_to_browser txt
+
+ nil
+ end
+ def self.send_to_browser txt
txt.gsub!("\n", '\n')
txt.gsub!('"', '\"')
code = "$('head').append(\"\")"
- Firefox.exec code
-
+ # Firefox.exec code
+ Browser.js code
nil
end
+
# When dups, make foo,foo be foo,foo:2 etc.
def self.add_nth_to_dups txt
- # Why hard-coded?
- # txt = "+ navbar navbar-inverse navbar-fixed-top/\n+ navbar-inner/\n+ container/\n+ container/\n+ hero-unit/\n"
-
dups = {}
txt = txt.split("\n")
txt.each do |line|
diff --git a/roots/curl.menu b/roots/curl.menu
new file mode 100644
index 00000000..1a30db06
--- /dev/null
+++ b/roots/curl.menu
@@ -0,0 +1,18 @@
+- headers/
+ =$ curl -i http://google.com
+ | only headers
+ =$ curl -I http://google.com
+- write to a file/
+ =$ curl http://www.yahoo.com -o y.txt
+ | Use file from url as filename
+ =$ curl http://www.yahoo.com -O
+- browser useragent/
+ =$ curl -A "Mozilla/5.0" http://microsoft.com
+ - more/
+ - Chrome 12 in OSX 10.6/
+ =$ curl -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30" http://microsoft.com
+ - iPhone/
+ =$ curl -A "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5" http://www.apple.com
+ - IE 7/
+ =$ curl -A "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" http://www.apple.com
+ =google/"curl -A" useragent
diff --git a/menu/current.rb b/roots/current.rb
similarity index 75%
rename from menu/current.rb
rename to roots/current.rb
index 87290086..3d9f5617 100644
--- a/menu/current.rb
+++ b/roots/current.rb
@@ -1,5 +1,6 @@
class Current
def self.menu *name
+ return "<< views/"
Buffers.current *name
end
end
diff --git a/roots/d3.rb b/roots/d3.rb
new file mode 100644
index 00000000..cfc2bbf0
--- /dev/null
+++ b/roots/d3.rb
@@ -0,0 +1,90 @@
+class D3
+
+ # v3 makes this not show the movement
+ # @d3/examples/movement/position/
+ # We're at .v2 now - go back to .v3 when this is fixed?
+ #
+ #
+
+ MENU = %`
+ | // Sample d3 code to modify and run
+ | svg.append("rect").attr("x", 40).attr("y", 40).attr("width", 60).attr("height", 60);
+ - docs/
+ - summary/
+ | This menu helps you use the d3 library, which renders
+ | awesome graphs and charts etc. in your browser.
+ - getting started/
+ | Expand the root "svg" menu and check out the one-line
+ | example. Try modifying the numbers and re-running it.
+ | Then look through the "examples" menu and do the same.
+ - urls/
+ - reference/
+ @https://github.com/mbostock/d3/wiki/API-Reference
+ - tutorials/
+ @https://github.com/mbostock/d3/wiki/Tutorials
+ - cool examples/
+ @http://blog.visual.ly/creating-animations-and-transitions-with-d3-js/
+ - see/
+ << @d3/
+ `
+
+ def self.render txt
+
+ html = ""
+ # html
+
+ # replace/
+ # |-http://d3js.org/d3.v2.min.js
+ # |+http://d3js.org/d3.v2.min.js
+ if txt !~ /
+
+ `.unindent
+ end
+
+ # If html...
+
+ if txt =~ /\A
+ var svg = d3.select("svg");
+ `.unindent
+ else
+
+ # If javascript...
+
+ html << %`
+
+ "
+ end
+
+ # if html = options[:html]
+ # html << html
+ # end
+ # if svg = options[:svg]
+ # html << "#{svg} "
+ # end
+
+ Xiki::Browser.html html, :name=>"d3", :via_os=>1
+ end
+
+ def self.menu_after output, *args
+
+ # Whenever there's no output, interject
+
+ # If it's quoted, run it in the browser (wrapping in d3 html if no there yet)
+ txt = args[-1]
+
+ return "=beg/quoted/" if txt =~ /^\|/ # If |..., ask for quote
+
+ return if txt !~ /\n/ # Return if not multi-line
+
+ self.render txt
+
+ "<* opened in browser!"
+ end
+end
diff --git a/lib/xiki/tools/databases.menu b/roots/databases.menu
similarity index 100%
rename from lib/xiki/tools/databases.menu
rename to roots/databases.menu
diff --git a/roots/date.rb b/roots/date.rb
new file mode 100644
index 00000000..146a9072
--- /dev/null
+++ b/roots/date.rb
@@ -0,0 +1,43 @@
+module Menu
+ class Date
+
+ MENU = %`
+ - .short/
+ - see/
+ =calendar/
+ =time/
+ `
+
+ def self.menu_after out, *args
+
+ if args == []
+ return "#{::Time.now.strftime("%B %-m, %Y")}\n#{out}"
+ end
+
+ # /..., so prepend time to top
+
+ nil
+ end
+
+ def self.short
+ "#{::Time.now.strftime("%Y-%m-%d")}"
+ end
+
+
+ end
+end
+
+
+# - local/
+# ! Time.now.strftime("%l:%M:%S %p %Z")
+# - time zone/
+# ! zone = args[0]
+# ! if ! zone
+# ! return ActiveSupport::TimeZone.us_zones.map(&:name).map{|o| "- #{o}/\n"}.join('')
+# ! end
+# !
+# ! zone.sub! /^\| /, ''
+# ! t = Time.now.in_time_zone ActiveSupport::TimeZone.new(zone)
+# ! t.strftime("%l:%M:%S %p %Z")
+# - city/
+# @google/what time is it in Chicago?
diff --git a/roots/db.rb b/roots/db.rb
new file mode 100644
index 00000000..07e2fb0b
--- /dev/null
+++ b/roots/db.rb
@@ -0,0 +1,14 @@
+require "#{Xiki.dir}roots/couch.rb"
+
+class Db
+ def self.create db
+ Couch.create db
+ end
+
+ def self.menu db=nil, *args
+ # If no args, output list
+ return Couch.databases if db.nil?
+
+ return Couch.docs db, *args
+ end
+end
diff --git a/roots/dbs.rb b/roots/dbs.rb
new file mode 100644
index 00000000..2edcfddc
--- /dev/null
+++ b/roots/dbs.rb
@@ -0,0 +1 @@
+Xiki['.mysql/dbs', args]
diff --git a/roots/deck.menu b/roots/deck.menu
new file mode 100644
index 00000000..f6a8e066
--- /dev/null
+++ b/roots/deck.menu
@@ -0,0 +1,25 @@
+- enable arrow keys/
+ ! Deck.enable_arrow_keys
+- docs/
+ - summary/
+ | Create a file with a ".deck" extension to create a lightweight
+ | presentation. The left and right arrow keys will go back and forth
+ | between the "slides".
+ |
+ | Make .deck files just like you make .xiki files, with sections divided
+ | by headings ("> foo" lines). The sections behave like slides. Only one
+ | section is shown at a time.
+ - keys/
+ | Use these keys to go back and forth between slides:
+ | - right+arrow+key: show next slide (hiding everything else)
+ | - left+arrow+key: show next slide
+ | - custom+reminder: jump to corresponding heading at end
+ - hints/
+ | To create and show hints that correspond to a section, create a section
+ | near the bottom of the file with the same heading but starting with
+ | ">>" instead of ">"
+ |
+ | Then, type custom+reminder to jump back and forth.
+ - to enable the deck keys in a .xiki file/
+ - type: do+keys+deck
+ - or use) @deck/enable arrow keys/
diff --git a/menu/def.rb b/roots/def.rb
similarity index 100%
rename from menu/def.rb
rename to roots/def.rb
diff --git a/menu/defs.rb b/roots/defs.rb
similarity index 85%
rename from menu/defs.rb
rename to roots/defs.rb
index f909056b..167bb01f 100644
--- a/menu/defs.rb
+++ b/roots/defs.rb
@@ -7,6 +7,8 @@ class Defs
@menu_path/
- .menus/
- .patterns/
+ - global/
+ - pre/
- .keys/
- docs/
- todo: pull menus from...!
@@ -65,23 +67,25 @@ def self.menus key=nil, source=nil
# /, so list menus...
if ! key
- keys = Menu.defs.keys.sort.map{|o| "+ #{o}/"}
+ keys = Command.defs.keys.sort.map{|o| "+ #{o}/"}
return keys.join "\n"
end
# /key/, so show def...
- implementation = Menu.defs[key]
+ implementation = Command.defs[key]
self.defs implementation, source
end
- def self.patterns key=nil, source=nil
+ def self.patterns category=nil, key=nil, source=nil
# /, so list menus...
+ hash = category == "pre" ? PrePattern.defs : Pattern.defs[:global]
+
if ! key
- keys = Pattern.defs[:global].keys.map{|o| "| #{o.source}"}
+ keys = hash.keys.map{|o| "| #{o.source rescue o}"}
return keys.join "\n"
end
@@ -89,8 +93,8 @@ def self.patterns key=nil, source=nil
regex = key[/^\| ?(.+)/, 1]
- found = Pattern.defs[:global].find{|k, v| k.source == regex}
- implementation = Pattern.defs[:global][found[0]]
+ found = hash.find{|k, v| k.source == regex}
+ implementation = hash[found[0]]
self.defs implementation, source
@@ -124,8 +128,8 @@ def self.defs implementation, source=nil
def self.clear
Pattern.defs.clear
- Menu.defs.clear
- ".flash - cleared!"
+ Command.defs.clear
+ "<* cleared!"
end
end
diff --git a/roots/deploy_el4r_gem.menu b/roots/deploy_el4r_gem.menu
new file mode 100644
index 00000000..6d8cc900
--- /dev/null
+++ b/roots/deploy_el4r_gem.menu
@@ -0,0 +1,13 @@
+- rubygems.org page/
+ @http://rubygems.org/gems/trogdoro-el4r
+- 1. update version/
+ @/projects/trogdoro-el4r/
+ $ git stash
+ - trogdoro-el4r.gemspec
+ | version: 1.0.
+ | date: 2012-
+- 2. build and deploy/
+ @/projects/trogdoro-el4r/
+ $ gem build trogdoro-el4r.gemspec
+ % gem push trogdoro-el4r-1.
+ $ git stash pop
diff --git a/roots/deploy_xiki_gem.menu b/roots/deploy_xiki_gem.menu
new file mode 100644
index 00000000..7e3d3fde
--- /dev/null
+++ b/roots/deploy_xiki_gem.menu
@@ -0,0 +1,14 @@
+- gemcutter page/
+ @http://gemcutter.org/gems/xiki
+- 1. update version/
+ =%xiki/
+ $ git stash
+ - xiki.gemspec
+ | s.version = "0.
+ | s.date = "
+ | s.add_dependency('trogdoro-el4r', [">= 1.0.
+- 2. build and deploy/
+ =%xiki/
+ $ gem build xiki.gemspec
+ % gem push xiki-0.
+ $ git stash pop
diff --git a/menu/des.rb b/roots/des.rb
similarity index 100%
rename from menu/des.rb
rename to roots/des.rb
diff --git a/roots/desktop_image.rb b/roots/desktop_image.rb
new file mode 100644
index 00000000..60a80974
--- /dev/null
+++ b/roots/desktop_image.rb
@@ -0,0 +1,38 @@
+# /, so give example...
+
+if args == []
+ return '
+ > Edit this svg and expand to set your desktop image
+ |
+ |
+ |
+ '
+end
+
+# /svg code, so set it to desktop...
+txt = '
+
+
+
+'
+txt = args[0]
+
+xml = %`
+
+
+ #{txt}
+
+ `.strip
+
+tmp_path = "/tmp/tmp.svg"
+File.open(tmp_path, "w") { |f| f << xml }
+
+png = "/tmp/desktop#{rand 99999}.png"
+
+`convert #{tmp_path} #{png}`
+
+Applescript.run %`
+ tell application "System Events" to set picture of every desktop to "#{png}"
+`.strip
+
+"<* set desktop"
diff --git a/lib/xiki/tools/dictionary.rb b/roots/dictionary.rb
similarity index 100%
rename from lib/xiki/tools/dictionary.rb
rename to roots/dictionary.rb
diff --git a/menu/dimensions/default.conf b/roots/dimensions/default.conf
similarity index 100%
rename from menu/dimensions/default.conf
rename to roots/dimensions/default.conf
diff --git a/roots/dimensions/dimensions_index.menu b/roots/dimensions/dimensions_index.menu
new file mode 100644
index 00000000..4a1dc124
--- /dev/null
+++ b/roots/dimensions/dimensions_index.menu
@@ -0,0 +1,17 @@
++ full
+ ! # Normal
+ ! View.dimensions_full
++ small
+ ! View.dimensions_set 100, 22, 60, 20
+ ! Styles.default_font_size
+ ! nil
++ new window
+ ! $el.new_frame
+ ! X"themes/list/Default"
+ ! View.dimensions_set 100, 22, 60, 20
+ ! #Cursor.white
++ current/
+ ! "
+ ! ! Styles.font_size #{Styles.get_font_size}
+ ! ! View.dimensions_set #{$el.frame_parameter(nil, :left)}, #{$el.frame_parameter(nil, :top)}, #{View.frame_width}, #{View.frame_height}
+ ! "
diff --git a/roots/dimensions/dimensions_index.rb b/roots/dimensions/dimensions_index.rb
new file mode 100644
index 00000000..4d4997bf
--- /dev/null
+++ b/roots/dimensions/dimensions_index.rb
@@ -0,0 +1,94 @@
+class Dimensions
+
+ def self.menu_before *args
+
+ options = yield
+ prefix = options[:prefix]
+
+ conf_file = File.expand_path "~/.xiki/roots/conf/dimensions.conf"
+
+ options[:no_search] = 1 if args[0] == "current"
+
+ # as+open...
+
+ if prefix == "open"
+
+ item = args[-1]
+
+ # It's open+...
+
+ raise "- dimensions.conf doesn't exist yet!" if ! File.exists? conf_file
+ View.open conf_file, :to=>"^[ +-]*#{$el.regexp_quote item}/?$" # "
+
+ return ""
+ end
+
+ # enter+all...
+
+ if prefix == "all" # enter+all
+ options[:no_search] = 1
+ return Tree.children File.read(conf_file), args
+ end
+
+ # if !..., eval it, or open
+
+ if args[-1] =~ /^! /
+
+ # If !..., we want all the lines
+ return "=beg/neighbors/" if args[-1] !~ /\n/
+
+ if prefix == "update" # as+open
+ self.update conf_file, *args
+ return ""
+ end
+
+ code = args[-1]
+ code.gsub! /^! /, ''
+ eval args[-1]
+ return ""
+ end
+
+ nil # Don't interject
+ end
+
+ def self.update conf_file, name, code
+ txt = File.read conf_file
+ txt = Tree.update txt, [name, code.strip]
+ File.open(conf_file, "w") { |f| f << txt }
+ end
+
+ def self.menu_after output, *args
+
+Ol "output", output
+ # /, so prepend user conf if there, and append =conf...
+
+ if args.blank?
+ conf = yield[:conf]
+ if conf # If conf, add item names defined by user
+ # conf.gsub! /^-/, '+'
+ items = conf.scan(/^[+-] .+/) # Just pull out items
+ items += output.split "\n" # Split into arrays and merge, so we can remove duplicates
+ items.uniq!
+ output = items.join "\n"
+ end
+
+ return "#{output}\n=conf/"
+ end
+
+ View.kill(:force_recent=>1) if View.name == "dimensions/" && args != ["current"]
+
+ # Run it through =conf output if matched...
+
+ # output is "" if it used a built-in item
+
+ if ! output
+ conf = yield[:conf]
+ txt = Tree.children conf, args
+ MenuHandler.eval_exclamations txt
+ return txt
+ end
+
+ # Do nothing
+ nil
+ end
+end
diff --git a/roots/dir.rb b/roots/dir.rb
new file mode 100644
index 00000000..666196f2
--- /dev/null
+++ b/roots/dir.rb
@@ -0,0 +1 @@
+"=" + Xiki.dir
diff --git a/menu/directions.menu b/roots/directions.menu
similarity index 100%
rename from menu/directions.menu
rename to roots/directions.menu
diff --git a/menu/directory.rb b/roots/directory.rb
similarity index 100%
rename from menu/directory.rb
rename to roots/directory.rb
diff --git a/roots/docs.menu b/roots/docs.menu
new file mode 100644
index 00000000..3086b2fc
--- /dev/null
+++ b/roots/docs.menu
@@ -0,0 +1 @@
+<< help/
diff --git a/roots/dom.rb b/roots/dom.rb
new file mode 100644
index 00000000..05f05878
--- /dev/null
+++ b/roots/dom.rb
@@ -0,0 +1,198 @@
+module Xiki
+ class Dom
+
+ def self.menu *args
+
+ # MENU string couldn't handle it, so delegate to dom path...
+
+ task = yield[:task]
+ return "* html\n* tidy\n* element\n* attributes\n* all divs" if task == []
+
+ options = {}
+
+ options[:tidy_html] = 1 if task && task == ["tidy"]
+ options[:element_html] = 1 if task && task == ["element"]
+ options[:attributes] = 1 if task && task == ["attributes"]
+
+ options[:html] = 1 if task && ["html", "all divs"].member?(task[0])
+
+ txt = self.dom *args, options
+
+ if task == ["all divs"]
+ txt = txt.split("\n").grep(/"
+ args = args.join ' > '
+
+ js = %`
+ $.fn.blink = function(times) {
+ console.log(times)
+
+ var el = $(this);
+ if(! times) times = 2;
+ for(x=1;x<=times;x++) {
+ el.animate({opacity: 0.0}, {easing: 'swing', duration: 200});
+ el.animate({opacity: 1.0}, {easing: 'swing', duration: 200});
+ }
+ return this;
+ };
+
+ var kids = [];
+ `.unindent
+
+ # dom/foo/, so blink and show children...
+
+ if options[:html]
+ result = Tree.pipe Browser.js("return $(\"#{args}\").html()")
+ return result
+ elsif options[:tidy_html]
+ result = Browser.js("return $(\"#{args}\").html()")
+ result = Html.tidy result
+ return Tree.pipe result
+ elsif options[:element_html]
+ result = Browser.js("return $(\"#{args}\")[0].outerHTML")
+ result = Html.tidy result
+ return Tree.pipe result
+ elsif options[:attributes]
+ result = Browser.js("return $(\"#{args}\")[0].outerHTML")
+ result = Html.tidy result, :wrap_attributes=>1
+ return Tree.pipe result
+ end
+
+ if ! save
+
+ # Otherwise, just blink and show children...
+
+ if prefix == :-
+ Browser.js "$(\"#{args}\").blink()"
+ return
+ end
+ if prefix != "all" && prefix != "outline"
+ js << %`
+ $("#{args}").blink().children().each(function(n, e){
+ var tag = e.nodeName.toLowerCase();
+ var id = $(e).attr('id');
+ if(id) tag += "#"+id
+ kids.push(tag);
+ })
+ `
+ end
+
+ js << %`
+ if(kids.length){
+ // return "yes";
+ return String(kids);
+ }else{
+ // return "no";
+ return "html::"+$("#{args}").html();
+ }
+ `.unindent
+
+ kids = Browser.js js #, :jquery=>1
+
+ kids = kids.sub(/\A"/, '').sub(/"\z/, '') if kids =~ /\A"/
+ if kids =~ /\Ahtml::/
+ kids = kids.sub(/\Ahtml::/, '').strip
+ return prefix == "all" ?
+ Tree.quote(kids) :
+ Html.tidy(kids, :tag=>raw_args).snippet
+ end
+
+ kids = kids.split ','
+
+ counts = {}
+ kids.each do |k| # Add on counts
+ k_raw = k.sub /#.+/, ''
+ counts[k] ||= 0
+ counts[k] += 1
+ k.replace "#{k}:#{counts[k]}" unless k =~ /#/ || counts[k] == 1
+ end
+ return kids.map{|o| "- #{o}/"}.join("\n")
+ end
+
+
+
+
+
+
+ # dom/foo/| quoted, so save to page...
+
+ save.gsub! "\n", "\\n"
+ save.gsub! '"', '\\"'
+
+ js << %`
+ var e = $(\"#{args}\");
+ if(e.length)
+ e.blink(1).html(\"#{save}\");
+ `.unindent
+
+ if args =~ /(.+) > (.+)/
+ parent, child = $1, $2.sub!(/:.+/, '')
+ js << %`
+ else
+ $(\"#{parent}\").blink(1).append(\"<#{child}>#{save}#{child}>\");
+ `.unindent
+ end
+
+ Browser.js js
+ Effects.glow :fade_in=>1
+
+ ""
+
+ end
+
+
+ def self.search txt=nil
+
+ if txt.nil?
+ return View.prompt "Enter a string to search for on the page"
+ end
+
+ js = %`
+ var result="", depth=-1;
+ $('*:contains(#{txt})').each(function(i, e){
+ var tag = e.tagName.toLowerCase();
+ e = $(e);
+ var current_depth = e.parents().length; // Only climb downward
+ if(current_depth <= depth) return depth = 9999; // Don't grab any more
+ depth = current_depth;
+ var prev_count = $(e).prevAll(tag).length;
+ result += tag;
+ if(prev_count > 0) result += ':'+(prev_count+1);
+ result += '/';
+ })
+ return result;
+ `.unindent
+
+ result = Browser.js js #, :jquery=>1
+
+ result.sub "html/", "= dom/"
+ end
+
+ end
+end
diff --git a/lib/xiki/tools/dotsies.rb b/roots/dotsies.rb
similarity index 84%
rename from lib/xiki/tools/dotsies.rb
rename to roots/dotsies.rb
index b2c378b7..0400a2f9 100644
--- a/lib/xiki/tools/dotsies.rb
+++ b/roots/dotsies.rb
@@ -1,6 +1,6 @@
module Xiki
class Dotsies
- def self.menu
+ MENU =
%`
|~this is an example
- .apply/
@@ -28,11 +28,10 @@ def self.menu
<< dotsies tweets/
<< youtube/use dotsies in transcript/
`
- end
def self.all_views
- View.kill if View.name == "menu" || View.name =~ /^@/
+ View.kill if View.name == "menu" || View.name =~ /^=/
faces = {
:default=>"monaco",
@@ -59,7 +58,7 @@ def self.all_views
end
def self.one_view
- View.kill if View.name == "menu" || View.name =~ /^@/
+ View.kill if View.name == "menu" || View.name =~ /^=/
$el.make_local_variable :dotsies_enabled
dotsies_enabled = $el.boundp(:dotsies_enabled) && $el.elvar.dotsies_enabled
@@ -84,13 +83,19 @@ def self.web_browser
end
def self.browse txt=nil
- return ".flash - Type some text" if txt.nil?
+ return "<* Type some text" if txt.nil?
"do"
end
def self.define_styles
+ # Styles.define :fontawesome, :size => '+10', :face => "FontAwesome"
+
Styles.define :dotsies, :size => '+2', :face => "Dotsies"
- Styles.define :dotsies_white, :size => '+2', :face => "Dotsies", :fg=>"fff"
+ # Styles.define :dotsies_white, :size => '+2', :face => "Dotsies", :fg=>"fff"
+
+ # Styles.define :dotsies_white, :size => '-2', :face => "Dotsies", :fg=>"bce"
+ Styles.define :dotsies_white, :size => '-2', :face => "Dotsies", :fg=>"cdf"
+
Styles.define :dotsies_experimental, :size => '+2', :face => "Dotsies Experimental"
Styles.define :dotsies_roman, :size => '+2', :face => "Dotsies Roman"
Styles.define :dotsies_mono, :size => '+2', :face => "Dotsies Mono"
diff --git a/roots/echo.rb b/roots/echo.rb
new file mode 100644
index 00000000..9b1cc879
--- /dev/null
+++ b/roots/echo.rb
@@ -0,0 +1,31 @@
+ancestors = options[:ancestors]
+menu_steps = ancestors && ancestors[-1] == "menu steps/"
+ancestors.pop if menu_steps
+
+options[:no_slash] = 1
+
+# / and no ancestors, so display message...
+
+if ! ancestors && args == []
+ return %`
+ : The 'echo' menu is just for experimenting with how
+ : paths work. Put some stuff underneath and expand them
+ : to see the path. You'll see the value that's passed
+ : into menus as the "path" variable.
+ - a/
+ - b/
+ :
+ <= options/
+ <= source/
+ `
+end
+
+# =echo/foo, so use foo as the path...
+
+path = args.any? ?
+ args.inspect :
+ options[:ancestors].inspect
+
+# ancestors/=echo, so use ancestors...
+
+"| #{path}"
diff --git a/roots/edited.rb b/roots/edited.rb
new file mode 100644
index 00000000..39047159
--- /dev/null
+++ b/roots/edited.rb
@@ -0,0 +1,6 @@
+# /, so show the list...
+
+files = DiffLog.file_list :tree_format=>1
+return "- No files edited recently in xsh" if files == []
+
+txt = files.join "\n"
diff --git a/menu/edits.rb b/roots/edits.rb
similarity index 100%
rename from menu/edits.rb
rename to roots/edits.rb
diff --git a/roots/effects.rb b/roots/effects.rb
new file mode 100644
index 00000000..b8014359
--- /dev/null
+++ b/roots/effects.rb
@@ -0,0 +1 @@
+Xiki[Xiki::Effects, args, options]
diff --git a/roots/elisp.rb b/roots/elisp.rb
new file mode 100644
index 00000000..77fb7064
--- /dev/null
+++ b/roots/elisp.rb
@@ -0,0 +1,26 @@
+# ~ or ~ manual, so handle
+
+return "* manual" if task == []
+if task == ["manual"]
+ $el.info "elisp"
+ return ""
+end
+
+# /, so give sample lisp...
+
+return "
+ | ; Provide some elisp to eval, like this
+ | (set 'a 2)
+ | (+ 1 a)
+ " if args == []
+
+# /code, so eval it...
+
+# Try inspecting by default
+result = $el.eval $el.read "(progn #{args[0]})"
+
+return if ! result
+
+result = $el.pp_to_string result
+
+Tree.quote result.to_s
diff --git a/menu/emacs/index.menu b/roots/emacs/index.menu
similarity index 70%
rename from menu/emacs/index.menu
rename to roots/emacs/index.menu
index 19bdc008..a982397e 100644
--- a/menu/emacs/index.menu
+++ b/roots/emacs/index.menu
@@ -7,5 +7,5 @@
- Prefix Keymaps/
- modes/
! Tree.quote $el.pp_to_string($el.elvar.auto_mode_alist)
-- lisp dirs/
- ! $el.elvar.load_path.to_a.map{|o| "@#{FileTree.add_slash_maybe o}\n"}.join('')
+- lisp load-path dirs/
+ ! $el.elvar.load_path.to_a.map{|o| "=#{FileTree.add_slash_maybe o}\n"}.join('')
diff --git a/menu/emacs/index.rb b/roots/emacs/index.rb
similarity index 100%
rename from menu/emacs/index.rb
rename to roots/emacs/index.rb
diff --git a/menu/emacs_config_tweaks/index.menu b/roots/emacs_config_tweaks/index.menu
similarity index 83%
rename from menu/emacs_config_tweaks/index.menu
rename to roots/emacs_config_tweaks/index.menu
index e06362ac..3684cc9c 100644
--- a/menu/emacs_config_tweaks/index.menu
+++ b/roots/emacs_config_tweaks/index.menu
@@ -1,6 +1,13 @@
> Summary
+- This file is out of date!
+ - as of > 2015-01-30
+
| Some miscellaneous emacs configuration. Expand items to see
-| what they do. Expand code to add to .emacs.
+| what they do. Then expand the code to add it to your .emacs.
+- make escape cancel things/
+ | ; Map escape to cancel (like C-g)...
+ | (define-key isearch-mode-map [escape] 'isearch-abort) ;; isearch
+ | (global-set-key [escape] 'keyboard-escape-quit) ;; everywhere else
- y shortcut for yes/
| ; Let me press "y", instead of type "yes"
| (fset 'yes-or-no-p 'y-or-n-p)
@@ -46,8 +53,3 @@
- emacsclient/
| ; Enable emacsclient (for telling emacs to view source, etc)
| (server-start)
-- control lock/
- | ; Like caps lock but for the control key. Use C-, to toggle it on and off.
- | (add-to-list 'load-path "~/.emacs.d/")
- | (require 'control-lock)
- | (control-lock-keys)
diff --git a/menu/emacs_config_tweaks/index.rb b/roots/emacs_config_tweaks/index.rb
similarity index 66%
rename from menu/emacs_config_tweaks/index.rb
rename to roots/emacs_config_tweaks/index.rb
index dcd703b0..0f47c306 100644
--- a/menu/emacs_config_tweaks/index.rb
+++ b/roots/emacs_config_tweaks/index.rb
@@ -24,13 +24,13 @@ def self.menu_after output, item=nil, content=nil
File.open(File.expand_path("~/.emacs"), "w") { |f| f << txt }
- # If it was the "control lock" item, copy config file over as well
- if item == "control lock"
- emacs_d = File.expand_path("~/.emacs.d")
- Dir.mkdir(emacs_d) if ! File.exists?(emacs_d)
- FileUtils.cp "#{Xiki.dir}menu/emacs_config_tweaks/control-lock.el", emacs_d
- end
-
- "@flash/- added this to your .emacs!"
+ # # If it was the "control lock" item, copy config file over as well
+ # if item == "control lock"
+ # emacs_d = File.expand_path("~/.emacs.d")
+ # Dir.mkdir(emacs_d) if ! File.exists?(emacs_d)
+ # FileUtils.cp "#{Xiki.dir}menu/emacs_config_tweaks/control-lock.el", emacs_d
+ # end
+
+ "<* added this to your .emacs!"
end
end
diff --git a/roots/emacs_setup/initrb_default.txt b/roots/emacs_setup/initrb_default.txt
new file mode 100644
index 00000000..f69780e0
--- /dev/null
+++ b/roots/emacs_setup/initrb_default.txt
@@ -0,0 +1,10 @@
+$LOAD_PATH << "{{xiki_dir}}lib/"
+require 'xiki'
+Xiki.init
+Xiki::KeyShortcuts.keys # Use default key shortcuts
+Xiki::Themes.use "Default" # Use xiki theme
+
+# Alternate configuration to just map Control+Return
+# (Comment out the KeyShortcuts.keys line above)
+# Xiki::KeyShortcuts.map_control_return
+
diff --git a/lib/xiki/tools/encoding.rb b/roots/encoding.rb
similarity index 67%
rename from lib/xiki/tools/encoding.rb
rename to roots/encoding.rb
index 302f8eb0..631e0d0e 100644
--- a/lib/xiki/tools/encoding.rb
+++ b/roots/encoding.rb
@@ -1,8 +1,6 @@
module Xiki
class Encoding
- def self.menu
- "
- - current: #{$el.elvar.buffer_file_coding_system.to_s}
+ MENU = "
- .dos/
- .raw unix/
- .iso unix/
@@ -10,26 +8,30 @@ def self.menu
- see/
<@ specials/
"
+
+ def self.menu_after out, *args
+ return if args.any?
+ "- current: #{$el.elvar.buffer_file_coding_system.to_s}\n#{out}"
end
def self.dos
$el.set_buffer_file_coding_system :dos
- ".flash - updated!"
+ "<* updated!"
end
def self.raw_unix
$el.set_buffer_file_coding_system :unix
- ".flash - updated!"
+ "<* updated!"
end
def self.iso_unix
$el.set_buffer_file_coding_system :iso_latin_1_unix
- ".flash - updated!"
+ "<* updated!"
end
def self.utf8
$el.set_buffer_file_coding_system :mule_utf_8_unix
- ".flash - updated!"
+ "<* updated!"
end
end
diff --git a/lib/xiki/tools/example.rb b/roots/example.rb
similarity index 51%
rename from lib/xiki/tools/example.rb
rename to roots/example.rb
index a48f4a52..d0e7a1f2 100644
--- a/lib/xiki/tools/example.rb
+++ b/roots/example.rb
@@ -7,7 +7,10 @@ module Xiki
# | p "123"
class Example
def self.menu *args
- Eval.menu *args
+ return "=beg/neighbors/" if args[-1] !~ /\n/
+ txt, out, exception = Code.eval args[-1].gsub(/^\| ?/, ''), :pretty_exception=>1
+ return exception if exception
+ Tree.quote out || txt
end
end
end
diff --git a/roots/expanders.rb b/roots/expanders.rb
new file mode 100644
index 00000000..e2a0c2e9
--- /dev/null
+++ b/roots/expanders.rb
@@ -0,0 +1,33 @@
+module Xiki
+ class Expanders
+
+ def self.menu *args
+
+ # Grab parent subpath to parse and pre-expand
+
+ options = yield
+ ancestors = options[:ancestors]
+ menu_steps = ancestors[-1] == "menu steps/"
+ ancestors.pop if menu_steps
+
+ path = ancestors.any? ? ancestors : options[:path]
+
+ expanders_result = Expander.expanders path
+
+ txt = self.ap expanders_result
+ txt.gsub! /^ /, ""
+ txt.sub! "[\n [", "[["
+ txt = Tree.quote txt, :char=>"|"
+
+ txt
+ end
+
+ def self.ap txt
+ txt = txt.ai
+ txt.sub! /\A{\n/, ''
+ txt.sub! /\n}\z/, ''
+ txt
+ end
+
+ end
+end
diff --git a/roots/f.xiki b/roots/f.xiki
new file mode 100644
index 00000000..6dfc9dd5
--- /dev/null
+++ b/roots/f.xiki
@@ -0,0 +1 @@
+<< files/
diff --git a/roots/face/face_index.menu b/roots/face/face_index.menu
new file mode 100644
index 00000000..cfd3d207
--- /dev/null
+++ b/roots/face/face_index.menu
@@ -0,0 +1,12 @@
++ complexion/
+ + light
+ + medium
+ + dark
++ eyes/
+ + brown
+ + blue
++ hair/
+ + conservative
+ + futuristic
+ + unicorn
++ reset
diff --git a/lib/xiki/tools/face.rb b/roots/face/face_index.rb
similarity index 84%
rename from lib/xiki/tools/face.rb
rename to roots/face/face_index.rb
index 15a4b31d..22cf919f 100644
--- a/lib/xiki/tools/face.rb
+++ b/roots/face/face_index.rb
@@ -9,13 +9,14 @@ def self.menu part, value=nil
File.open("/tmp/face.yml", "w") { |f| f << hash.to_yaml }
html = "
-
+
".unindent
hash.keys.sort.each{|key| html << "
\n"}
html << ""
Browser.html html
+ ""
end
end
end
diff --git a/roots/facts.rb b/roots/facts.rb
new file mode 100644
index 00000000..e15f2903
--- /dev/null
+++ b/roots/facts.rb
@@ -0,0 +1 @@
+Xiki["memorize", args, options]
diff --git a/roots/faq.menu b/roots/faq.menu
new file mode 100644
index 00000000..a442cc36
--- /dev/null
+++ b/roots/faq.menu
@@ -0,0 +1,27 @@
+: What key shortcuts are like Ctrl+A, Ctrl+E and Ctrl+K in bash?
+ | ^A ^A : Beginning of line
+ | ^E ^E : End of line
+ | ^K ^K : Kill line
+ |
+ : If the alt or option key is configured in your terminal...
+ | You can do Alt+A, Alt+E, and Alt+K ("option" instead of "alt"
+ | on the mac).
+ |
+ | You may need to configure your keyboard preferences in your
+ | console app to make alt (or option) behave in the standard way.
+ | The preference will be called something like "make alt behave
+ | like escape+".
+ |
+ |
+: Why aren't there more questions in this faq?
+ | It's a work in progress. More will be added later!
+ |
+: How can I contribute to this faq?
+ | Edit
/roots/faq.menu, commit, and send a pull
+ | request.
+ |
+: What are the next steps for this readme file, to make it more accessible?
+ | 1. Mirror to a markdown file
+ | 2. Link to it from the README, so it'll be more visible
+ | - https://help.github.com/articles/relative-links-in-readmes
+ |
diff --git a/roots/files.rb b/roots/files.rb
new file mode 100644
index 00000000..919325c1
--- /dev/null
+++ b/roots/files.rb
@@ -0,0 +1,87 @@
+file = Bookmarks["%links"]
+
+# Grab first few files from :n, regardless of how we proceed...
+
+limit = 100
+list, dir = [], []
+IO.foreach(file) do |line|
+
+ indent = Tree.indent_size line
+
+ # .../, so append as dir...
+
+ if line =~ /^[^:|#]*\/$/
+ dir = dir[0..indent]
+ next dir[indent] = line
+ end
+
+ filename = line[/^ *[+-] (.+\w)$/, 1]
+ next if ! filename
+
+ # It's a filename, so add dir/file to output...
+
+ dir.compact!
+
+ dir.map!{|o| Line.without_label :line=>o.strip}
+ dir = dir[0..indent]
+ full = dir
+ full[indent] = filename
+ full = full.join
+ full = Bookmarks[full]
+
+ next if list.member? full # Skip if we've already added this one
+
+ list << full
+ limit -= 1
+ break if limit < 1
+end
+
+# /, so list most recent files from :n...
+
+if args == []
+ # Do hotkeys if launched by key shortcut (which we can tell because of the view name)
+ options[:hotkey] = 1 if View.name == "files/"
+
+ return list.map{|o| "+ #{o.sub(/.+\//, '')}"}.join "\n"
+end
+
+# /foo.txt, so find file path from list and go to it...
+
+# Handle tasks
+
+return "* show in links\n* to top" if task == []
+
+if task == ["show in links"]
+ View.open "%links"
+ Search.forward "^[ +-]* #{Search.quote_elisp_regex args[0]}", :from_top=>1
+ return ""
+
+elsif task == ["to top"]
+
+ # Jump to this file in the nav
+ View.open "%links"
+ Search.forward "^[ +-]* #{Search.quote_elisp_regex args[0]}", :from_top=>1
+
+ # Jump up to the heading
+ Notes.to_block :up=>1
+
+ # Move heading to the top
+ modified = View.modified? # Remember if nav was modified
+ Notes.move_block_to_top :no_fade=>1
+ DiffLog.save :no_diffs=>1 if ! modified # Save changes (unless it was modified)
+
+ # Re-display the list (so it'll be at the top)
+ Launcher.open("files/")
+
+ return "" #"- todo > implement . ~ to top"
+
+end
+
+found = list.find{|o| o =~ /\/#{Regexp.escape args[0]}$/}
+
+# /file, so find file from :n and open it...
+
+View.open found
+
+nil
+
diff --git a/roots/filter.rb b/roots/filter.rb
new file mode 100644
index 00000000..4663435a
--- /dev/null
+++ b/roots/filter.rb
@@ -0,0 +1,26 @@
+# First, maybe find code that does "##"
+# and use =f instead?
+# (Make launcher use @f as special case if file tree.)
+class Filter
+ def self.menu *args
+ options = yield
+ ancestors = options[:ancestors]
+
+ # Stitch our args onto the ancestor before calling!
+ ancestors[-1] << "#{args[1..-1].join('/')}/" if args.length > 1
+
+ txt = Expander.expand ancestors
+
+ return txt if args.length > 1 # If there were args, just echo through (filtering happened at higher level
+
+ txt = txt.split("\n").select{|o|
+ o.sub(/\A[ +-]+/, '') =~ /#{args[0]}/ ||
+ o =~ /#{args[0]}/i
+ }.join("\n")
+
+ txt
+
+ # TODO > handle multiple filter args?
+
+ end
+end
diff --git a/roots/font.menu b/roots/font.menu
new file mode 100644
index 00000000..35213a9b
--- /dev/null
+++ b/roots/font.menu
@@ -0,0 +1 @@
+<< styles/
diff --git a/menu/fontawesome.menu b/roots/fontawesome.menu
similarity index 97%
rename from menu/fontawesome.menu
rename to roots/fontawesome.menu
index 2fc04ab4..ab3fabe1 100644
--- a/menu/fontawesome.menu
+++ b/roots/fontawesome.menu
@@ -1,5 +1,5 @@
- all/
- @bootstrap/
+ =bootstrap/
+ icon/
- expand-alt
- smile
@@ -343,7 +343,7 @@
- stethoscope
- user-md
- category/
- @bootstrap/
+ =bootstrap/
- project name/all fontawesome icons
h4/New Icons
icon/
@@ -727,7 +727,7 @@
stethoscope
user-md
- sizes/
- @bootstrap/
+ =bootstrap/
- project name/fontawesome icon sizes
> star-empty
3x
@@ -833,4 +833,17 @@
2x
large
+- inline/
+ ! txt = ""
+ ! a = ""
+ ! 63.times { txt << "|*#{a}\n"; a = a.next }
+ ! #txt << "|-----------------\n"
+ ! a = ""
+ ! 30.times { txt << "|*#{a}\n"; a = a.next }
+ ! a = "ïƒ "
+ ! 30.times { txt << "|*#{a}\n"; a = a.next }
+ ! a = "ï„–"
+ ! 40.times { txt << "|*#{a}\n"; a = a.next }
+ ! txt
+
diff --git a/roots/foo.menu b/roots/foo.menu
new file mode 100644
index 00000000..5716ca59
--- /dev/null
+++ b/roots/foo.menu
@@ -0,0 +1 @@
+bar
diff --git a/roots/gem.rb b/roots/gem.rb
new file mode 100644
index 00000000..e38ecfd7
--- /dev/null
+++ b/roots/gem.rb
@@ -0,0 +1,90 @@
+module Xiki::Menu
+ class Gem
+
+ MENU = "
+ - */
+ - */
+ - .readme/
+ - .source/
+ - .uninstall/
+ - .environment/
+ - upgrade rubygems/
+ = % gem update --system
+ = % gem update --system 1.8.24
+ "
+
+ def self.menu_after output, *args
+
+ # =commit/gems > made menu nestable, show gems in menu root.
+
+ # output from MENU, so do nothing...
+
+ return nil if output && args.any?
+
+ dir = Tree.dir || "/tmp/"
+ name = args[0]
+
+ # /, so prepend list of gems...
+
+ if ! name
+ version = Shell.sync "gem -v", :dir=>dir
+ gem_list = Shell.sync "gem list", :dir=>dir
+ gem_list = gem_list.gsub(/ \(.+/, "").gsub(/.+/, "+ \\0/")
+ gem_list = "|- no gems installed!\n" if gem_list == "\n"
+ return "| rubygems #{version}#{gem_list}| Options:\n#{output}"
+ end
+
+ # /gem, so list versions...
+
+ txt = Shell.sync "gem list #{name}", :dir=>dir
+ versions = txt[/\((.+)\)/, 1]
+ versions = versions.split ", "
+
+ versions.map{|o| "- #{o}/\n"}.join
+ end
+
+ def self.gem_dir name
+ # TODO: use .find_all_by_name instead, and grab the appropreate version
+ # Probably require version as a param to this method.
+ ::Gem::Specification.find_by_name(name).gem_dir+"/"
+ end
+
+ def self.readme name, version
+
+ dir = Gem.gem_dir name
+
+ entries = Dir.new(dir).entries
+ file = entries.find{|o| o =~ /^readme/i}
+
+ return "| No readme file found in\n= #{dir}" if ! file
+
+ path = "#{dir}#{file}"
+
+ Tree << "= #{path}"
+
+ Launcher.enter_all
+ ""
+ end
+
+ def self.source name, version
+ dir = Gem.gem_dir name
+
+ "= #{dir}"
+ end
+
+ def self.uninstall name, version
+ # txt = "sudo gem uninstall #{name} -v #{version}"
+ txt = "gem uninstall #{name} -v #{version}"
+ Shell.run txt, :dont_move=>1
+
+ "=flash/- Running gem uninstall command in shell..."
+ end
+
+ def self.environment
+ dir = Tree.dir || "/tmp/"
+ Shell.sync("gem environment", :dir=>dir).strip.gsub(/^/, '| ')
+ ""
+ end
+
+ end
+end
diff --git a/roots/gems.menu b/roots/gems.menu
new file mode 100644
index 00000000..69972e06
--- /dev/null
+++ b/roots/gems.menu
@@ -0,0 +1 @@
+<< gem/
diff --git a/menu/git.rb b/roots/git.rb
similarity index 57%
rename from menu/git.rb
rename to roots/git.rb
index 8c106801..ef98e905 100644
--- a/menu/git.rb
+++ b/roots/git.rb
@@ -5,8 +5,8 @@ class Git
MENU = "
- .diff/
- - .log/
- .status/
+ - .log/
- .graph/
- .setup/
- .create/
@@ -22,9 +22,13 @@ class Git
- .diff/
- .commit/
- .add/
- - .delete/
+ - .remove/
- .revert/
- .unadd/
+ - .status/
+ - .commit/
+ - .add/
+ - .remove/
"
def self.menu_before *args
@@ -33,10 +37,7 @@ def self.menu_before *args
options = yield
- dir = Tree.closest_dir options[:ancestors]
-
- # Tell them to nest under something if not nested...
- return self.docs if ! dir
+ dir = options[:dir]
exists, kind = FileTree.examine dir # => [false, :file]
@@ -47,10 +48,9 @@ def self.menu_before *args
return FileTree.suggest_mkdir dir
end
- branch = Xiki::Git.branch_name dir
+ branch = Xiki::Git.branch_name dir #> |||
# If not a repo, suggest creating one...
-
if args[0] != "setup" && branch.nil?
return "| Not a git repository. Create a new one here?\n- setup/create/"
end
@@ -59,6 +59,7 @@ def self.menu_before *args
end
def self.menu_after output, *args
+
return if args.any?
# If /, add push/thebranch/ to the beginning
@@ -68,18 +69,18 @@ def self.menu_after output, *args
end
def self.diff *args
+
options = yield
options[:no_search] = 1
- return "@prompt/Type a commit message." if args == ["commit"]
-
options[:no_slash] = 1
- dir = Tree.closest_dir options[:ancestors]
+ dir = options[:dir]
quote = args.pop if args[-1] =~ /^\|/
+ quote = args.pop if args[-1] =~ /^:/
path = args.any? ? args.join("/") : nil
- quote = quote.sub(/^\|/, '') if quote
+ quote = quote.sub(/^:/, '') if quote
# If we're nested under a file, break up into parts
if File.file? dir
@@ -90,40 +91,155 @@ def self.diff *args
end
- def self.do_status
- dir = Keys.bookmark_as_path :prompt=>"Enter a bookmark to show git status: "
+ def self.status *args
- menu = "
- #{dir}
- - @git/
- + status/
- ".unindent.strip
- Launcher.open(menu)
+ options = yield
+ dir = options[:dir]
+
+ # /status, so list files...
+
+ if args == []
+
+ txt = Shell.command("git status .", :dir=>dir)
+ txt = Tree.quote txt
+ txt.gsub! /^: #/, ':'
+
+ txt.gsub! /^:\t(modified|deleted|renamed|new file): +/, "+ \\1: "
+
+ txt.gsub! /^:\t/, "+ "
+
+ # Add blank space above unstaged, but only if both added and unadded files
+ txt.gsub!(/\n:\n: (Changes|Untracked) /, "\n\n:\n: \\1 ") #if txt =~ /: Changes to be committed:\n/
+
+ txt.sub! /^\+ /, "+ commit/\n\\0"
+
+ return txt
+ end
+
+ label = args[0].slice!(/^.+?: /)
+
+ quote = args.pop if args[-1] =~ /^:/
+ file = args.join "/"
+
+ # /status/a.txt, so show diff or contents...
+
+ if ! quote
+
+ task = options[:task]
+
+ return "* add\n* add multiple\n* remove\n* unadd\n* revert" if task == []
+ return self.tasks_item task, file, dir if task
+
+ # "modified:", so show diff
+ if label == "modified: "
+ txt, error = self.diff_internal("git diff --patience --relative #{self.git_diff_options} #{file}", dir)
+ txt = "-no changes" if txt == ""
+
+ txt.gsub!(/^/, ':')
+ txt.gsub!(/^:(---|\+\+\+|diff|index) .+\n/, '')
+ return txt
+ end
+
+ if label == "renamed: "
+ file.sub! /.+ -> /, '' # Remove the first file
+ txt, error = self.diff_internal("git diff --patience --relative #{self.git_diff_options} #{file}", dir)
+ txt = "-no changes" if txt == ""
+ txt.gsub!(/^/, ':')
+ txt.gsub!(/^:(---|\+\+\+|diff|index) .+\n/, '')
+ return txt
+ end
+
+ # "deleted:", so show old file
+
+ if label == "deleted: "
+ txt = Shell.command("git show HEAD:#{file}", :dir=>dir)
+ return txt.gsub!(/^/, ':-')
+ end
+
+ # a.txt (no label), so just show from disk...
+
+ if ! label || label == "new file: "
+ txt = File.read("#{dir}#{file}").gsub(/^/, ":+")
+ txt = ":@@ +1\n#{txt}"
+ return txt
+ return "todo > show from disk"
+ end
+
+ # We don't know the label
+
+ return "Don't understand the label: #{label}"
+ end
+
+ # /status/a.txt/:content, so navigate to the file...
+
+ file.sub! /.+ -> /, '' # In case it's "renamed: a -> b"
+
+ whole_path = "#{dir}#{file}"
+ self.jump_to_file_in_tree whole_path
+
+ return ""
- nil
end
- def self.status *args
- self.diff({:expand=>false}, *args)
+ def self.tasks_item task, file, dir
+
+ if task == ["add"]
+ command = "git add \"#{file}\""
+ txt = Shell.sync command, :dir=>dir
+ return Tree.quote(txt) if txt.any?
+ return "<* added!"
+ end
+
+ if task == ["add multiple"]
+ indent = Line.indent
+ Line.to_left
+ View.<< "#{indent}+ add/\n", :dont_move=>1
+ return ""
+ end
+
+ if task == ["remove"]
+ command = "git rm -r \"#{file}\""
+ txt = Shell.sync command, :dir=>dir
+ return "<* removed!"
+ end
+
+ if task == ["unadd"]
+ command = "git reset \"#{file}\""
+ txt = Shell.sync command, :dir=>dir
+ return "<* it was reset!"
+ end
+
+ if task == ["revert"]
+ command = "git checkout \"#{file}\""
+ txt = Shell.sync command, :dir=>dir
+ return "<* file reverted!"
+ end
+
end
+
def self.status_raw
- dir = Tree.closest_dir yield[:ancestors]
+ dir = options[:dir]
- result = Console.sync "echo 'TODO - finish .status_raw - #{dir}'" #, :dir=>Dir.pwd______
+ result = Shell.sync "echo 'TODO - finish .status_raw - #{dir}'"
Tree.quote result
end
def self.make_sample_files
- dir = Tree.closest_dir yield[:ancestors]
+ options = yield
+ dir = options[:dir]
- Dir.mkdir "#{dir}/d" rescue nil
+ Dir.mkdir "#{dir}d" rescue nil
txt = "hello\nhi again\n"
- ["a.txt", "b.txt", "d/aa.txt"].each { |path| File.open("#{dir}/#{path}", "w") { |f| f << txt } }
+ filenames = ["a.txt", "b.txt", "d/aa.txt"]
+
+ filenames.each { |path| File.open("#{dir}#{path}", "w") { |f| f << txt } }
+
+ Shell.command "git add #{filenames.join ' '}", :dir=>dir
"
- | Created these files:
+ | Created and added these files:
| - a.txt
| - b.txt
| - d/
@@ -150,37 +266,41 @@ def self.push branch
return "- choose the branch!"
end
- dir = Tree.closest_dir yield[:ancestors]
+ options = yield
+ dir = options[:dir]
self.push_internal branch, dir
nil
end
- def self.add
- dir = Tree.closest_dir yield[:ancestors]
+ def self.add # options=nil
+ options = yield
+ dir = options[:dir]
siblings = Tree.siblings
- return "- No files to add (they should be siblings of add)!" unless siblings.any?
siblings = self.remove_options siblings
+ return "- No files to add (they should be siblings of add)!" unless siblings.any?
+
command = "git add #{siblings.join("\\\n ")}"
- txt = Console.sync command, :dir=>dir
+ txt = Shell.sync command, :dir=>dir
return Tree.quote(txt) if txt.any?
- "@flash/- added!"
+ "<* added!"
end
def self.commit message=nil
- return View.prompt "Enter a commit message" if message.nil?
+ return " Enter a commit message" if message.nil?
- dir = Tree.closest_dir(yield[:ancestors])
+ options = yield
+ dir = options[:dir]
self.commit_internal message, dir
end
def self.methods_by_date path
- txt = Console.sync "git blame \"#{path}\""
+ txt = Shell.sync "git blame \"#{path}\""
txt = txt.split "\n"
txt = txt.select{|o| o =~ /\) *def /} # Remove all but method definitions
@@ -195,9 +315,18 @@ def self.methods_by_date path
- def self.diff_internal command, dir
- txt = Console.sync command, :dir=>dir
+ def self.diff_internal command, dir, options={}
+
+ txt, error = nil, nil
+ if options[:txt]
+ txt = options[:txt]
+ else
+ txt, error = Shell.command command, :dir=>dir, :return_error=>1
+ end
+ return ["", error] if error
+
+ # Show intra-line diffs > not used any more
if Keys.prefix_u
txt.gsub!(/\c[\[31m(.*?)\c[\[m/, "\(\-\\1\-\)")
txt.gsub!(/\c[\[32m(.*?)\c[\[m/, "\(\+\\1\+\)")
@@ -213,73 +342,27 @@ def self.diff_internal command, dir
txt.gsub! /\([+-][+-]\)/, ''
else
txt.gsub! /^ $/, ''
+ # Delete redundant lines and format some as gray
+ txt.gsub! /^new file mode .+\n/, ""
+ txt.gsub! /^deleted file mode /, "@@ \\0"
end
- txt
- end
-
- def self.status_internal txt
- txt.gsub!(/^#\t(.+?): +/, "- \\1: ")
- txt.gsub!(/^#\t/, "- ")
- txt.gsub!(/^#\n/, '')
- txt.gsub!(/^#/, '|')
- txt.gsub! /.+ \.\..+\n/, ""
- txt
- end
-
- # Takes as input the output of .status_internal.
- def self.status_to_hash txt
- result = {}
-
- # Pull out unadded
- unadded = txt[/^\| Changed but not updated:.+/m]
- result[:unadded] =
- if unadded
- unadded.sub! /\A(^\|[^\n]+\n)+/m, '' # Remove first few headings
- unadded.sub! /^\|.+/m, '' # Remove future sections
- unadded.scan(/^- (.+?): (.+)/)
- else
- []
- end
-
- # Pull out added
- added = txt[/^\| Changes to be committed:.+/m]
- result[:added] =
- if added
- added.sub! /\A(^\|[^\n]+\n)+/m, '' # Remove first few headings
- added.sub! /^\|.+/m, '' # Remove future sections
- added.scan(/^- (.+?): (.+)/)
- else
- []
- end
-
- # Pull out untracked
- untracked = txt[/^\| Untracked files:.+/m]
- result[:untracked] =
- if untracked
- files = untracked.scan(/^- (.+)/)
- files.map!{|i| ['untracked', i[0]]}
- else
- []
- end
-
- result
+ [txt, error]
end
-
def self.graph *args
- dir = Tree.closest_dir(yield[:ancestors])
+ options = yield
+ dir = options[:dir]
- # TODO: finish - Delegate to .log!
+ txt = Shell.sync %`git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"`, :dir=>dir
- "todo"
+ Tree.quote txt
end
def self.log *args
-
options = yield
- dir = Tree.closest_dir(options[:ancestors])
+ dir = options[:dir]
toplevel, relative = Xiki::Git.toplevel_split dir
@@ -293,15 +376,16 @@ def self.log *args
if rev.nil?
- # command = "git log -1000 --pretty=oneline"
- command = "git log -1000 --oneline"
+ command = "git log --follow -1000 --oneline"
command << " '#{relative}'" if relative
+ txt = Shell.command command, :dir=>toplevel
- txt = Console.run command, :sync=>true, :dir=>toplevel
+ return "> No revisions in the repository yet?\n: It looks like this is a new repository. Do a commit first,\n: then try running 'log' again." if txt == "\n> error\nfatal: bad default revision 'HEAD'\n"
txt.gsub! ':', '-'
txt.gsub! /(.+?) (.+)/, "\\2) \\1/"
txt.gsub! /^- /, ''
+
return txt.gsub!(/^/, '+ ')
end
@@ -319,15 +403,13 @@ def self.log *args
# /rev/, so show files for rev...
if ! file
- # command = "git show --pretty=oneline --name-status #{rev}"
relative_flag = relative ? "--relative=#{relative}" : ''
command = "git diff --pretty=oneline --name-status #{relative_flag} #{rev}~ #{rev}"
# Rev passed, so show all diffs
- txt = self.diff_internal command, toplevel
+ txt, error = self.diff_internal command, toplevel
- # txt.sub! /^.+\n/, '' # Remove 1st line?
txt.gsub! /^([A-Z]+)\t/, "\\1) "
txt.gsub! /^M\) /, ''
return txt.split("\n").sort.map{|l| "+ #{l}\n"}.join('')
@@ -349,21 +431,20 @@ def self.log *args
# Probably broken - hasn't been tested since Unified refactor
minus_one = prefix == 0 ? "~" : ""
- txt = Console.run("git show #{rev}#{minus_one}:#{file}", :sync=>true, :dir=>toplevel)
- ENV['no_slash'] = "1"
+ txt = Shell.run("git show #{rev}#{minus_one}:#{file}", :sync=>true, :dir=>toplevel)
+
return Tree.quote txt
end
command = "git show #{@@git_diff_options} --pretty=oneline #{rev} -- #{file}"
- txt = self.diff_internal command, toplevel
+ txt, error = self.diff_internal command, toplevel
txt.sub!(/.+?@@/m, '@@')
txt.gsub! /^/, '|'
- ENV['no_slash'] = "1"
+
return txt
end
-
# /rev/file/line, so jump to file...
whole_path = is_file ? dir : "#{toplevel}/#{file}"
@@ -388,9 +469,9 @@ def self.jump_to_file_in_tree file
orig = View.cursor
- Search.backward "^ +\|@@" unless Line.matches(/^ +\|@@/)
+ Search.backward "^ +[|:]@@" unless Line.matches(/^ +[|:]@@/)
inbetween = View.txt(orig, View.cursor)
- inbetween.gsub!(/^ +\|-.*\n/, '')
+ inbetween.gsub!(/^ +[|:]-.*\n/, '')
inbetween = inbetween.count("\n")
line = Line.value[/\+(\d+)/, 1]
@@ -400,72 +481,46 @@ def self.jump_to_file_in_tree file
View.to_line(line.to_i + (inbetween - 1))
end
- # Called by code tree directly
- def self.status_tree project
- dir = self.extract_dir project
- dir = Bookmarks.expand(dir)
- CodeTree.tree_search_option + self.status_tree_internal(dir)
- end
-
- def self.status_tree_internal dir
-
- status = Console.run "git status", :dir => dir, :sync => true
-
- # Grab out modified:...
- found = status.scan(/^#\s+modified: +(.+)/)
- result = []
- found.each do |m|
- result << "#{dir}#{m[0]}"
- end
- Tree.paths_to_tree(result)
- end
-
def self.clean! txt
txt.gsub!(/^ ?index .+\n/, '')
txt.gsub!(/^ ?--- .+\n/, '')
txt.gsub!(/^ ?\+\+\+ .+\n/, '')
end
- def self.extract_dir project
- project.sub(/.+? - /, '').sub(/\/$/, '')
- end
-
-
def self.git_diff dir, file, line, options={}
- is_unadded = false
+ is_unadded = false # This is now hard-coded. It used to enable a mode for bypassing adding and commtting directly?
# /, so show diff of files...
if file.nil?
- txt = Console.run "git status", :dir=>dir, :sync=>true
- hash = self.status_to_hash(self.status_internal(txt))
+ txt = Shell.command "git status --porcelain", :dir=>dir
+ hash = Xiki::Git.status_to_hash txt
- untracked = hash[:untracked].map{|i| i[1]}
- untracked.map!{|i| "+ untracked) #{i}\n"}
+ untracked = hash[:untracked]
+
+ untracked.map!{|i| "+ untracked) #{i[1]}\n"}
option = is_unadded ? "- add\n" : "- commit/\n"
- # command = "git diff --patience --relative #{self.git_diff_options} #{is_unadded ? '' : ' HEAD'}"
command = "git diff -b --patience --relative #{self.git_diff_options} #{is_unadded ? '' : ' HEAD'}"
is_file = File.file? dir
- txt = self.diff_internal command, dir
- # Ol.ap txt
+ txt, error = self.diff_internal command, dir
- if txt =~ /^fatal: ambiguous argument 'HEAD': unknown revision/
+ if error =~ /^fatal: ambiguous argument 'HEAD': unknown revision/
txt = self.status_hash_to_bullets hash, is_unadded
else
unless txt.empty?
self.clean! txt
- txt.gsub!(/^/, ' |')
+ txt.gsub!(/^/, ' :')
if is_file
return txt.sub(/.+\n/, '') # First "diff..." line deleted
end
- txt.gsub!(/^ \| ?diff --git .+ b\//, '- ')
+ txt.gsub!(/^ : ?diff --git .+ b\//, '- ')
end
end
@@ -481,13 +536,24 @@ def self.git_diff dir, file, line, options={}
end
end
- txt << untracked.join("")
+ # No files in project, so suggest they create some...
+
+ if Dir["#{dir}*"] == []
+ return "
+ | No files exist. Try creating some and adding them to the repo.
+ |
+ | This creates and adds a few sample files automatically:
+ =git/setup/make sample files/
+ ".unindent
+ end
+
+ txt << untracked.join("") if untracked
return "
- | There were no differences. Try modifying a file first.
- | Or create a few sample files:
- @git/setup/make sample files/
+ | There were no differences to added files. Try modifying some files first,
+ | or adding some unadded files.
".unindent if ! txt.any?
+
return option + txt + "
- add/
- delete/
@@ -496,15 +562,21 @@ def self.git_diff dir, file, line, options={}
".unindent
end
-
# /file, so show diff...
if line.nil? # If no line passed, re-do diff for 1 file
- txt = is_unadded ?
+ txt, error = is_unadded ?
self.diff_internal("git diff --patience --relative #{self.git_diff_options} #{file}", dir) :
- # self.diff_internal("git diff --patience --relative #{self.git_diff_options} HEAD #{file}", dir)
self.diff_internal("git diff --patience -b --relative #{self.git_diff_options} HEAD #{file}", dir)
+
+ # File doesn't exist in repo, so just show its contents...
+
+ if error =~ /^fatal: ambiguous argument 'HEAD'/
+ txt = File.read "#{dir}#{file}"
+ txt.gsub! /^/, ' '
+ end
+
self.clean! txt
if txt.blank?
@@ -512,13 +584,13 @@ def self.git_diff dir, file, line, options={}
# If not in repository, just show the contents
if ! file_in_repository
- return "| untracked file...\n|@@ +1\n" + File.read(Bookmarks.expand("#{dir}/#{file}")).gsub(/^/, '|+').gsub("\c@", '.')
+ return "| untracked file:\n|@@ +1\n" + File.read(Bookmarks.expand("#{dir}/#{file}")).gsub(/^/, '|+').gsub("\c@", '.')
end
return "| No diffs"
end
txt.gsub!(/^ ?diff .+\n/, '')
- txt.gsub!(/^/, '|')
+ txt.gsub!(/^/, ':')
self.jump_line_number_maybe txt, options
@@ -536,17 +608,17 @@ def self.git_diff dir, file, line, options={}
def self.jump_line_number_maybe txt, options
- line_number = Xiki::Git.jump_line_number
- return if ! line_number
+ line_found = options.delete :line_found
+ return if ! line_found
# Get rid of this? What did it do? Possibly an early version of the diff where I made the line numbers parents items?
last = 0
target_line, target_boundary = 0, 0
txt.split("\n").each_with_index do |o, i|
- target_line += 1 if o =~ /^\|[+ ]/
- match = o[/^\|@@ .+\+(\d+)/, 1]
- if target_line >= line_number
+ target_line += 1 if o =~ /^:[+ ]/
+ match = o[/^:@@ .+\+(\d+)/, 1]
+ if target_line >= line_found
break
end
if match
@@ -557,7 +629,7 @@ def self.jump_line_number_maybe txt, options
last += 3
last += (target_line - target_boundary)
- options[:jump_line_number] = last
+ options[:line_found] = last
end
@@ -571,31 +643,47 @@ def self.status_hash_to_bullets hash, is_unadded
end
def self.push_internal dest, dir
- Console.run "git push origin #{dest}", :dir=>dir
+
+ # Todo > only cd if necessary!
+
+ DiffLog.quit_and_run "cd \"#{dir}\"\ngit push origin #{dest}"
+
nil
end
def self.remove_options siblings
- siblings.select{|o| o !~ /^(commit|delete|revert|add|unadd)\// && o !~ /^\|/ }
+ siblings = siblings.map{|l| l.sub /^[+-] /, ""}
+ siblings.map!{|l| l.sub /^[a-z ]+: /, ""}
+ siblings = siblings.select{|o| o !~ /^(commit|delete|revert|add|unadd|remove)\// && o =~ /^\w/}
+ siblings.map!{|o| o.sub(/^(modified|deleted|renamed): /, '')}
+ siblings.map!{|o| o.sub(/^new file\) /, '')}
+ siblings
end
def self.commit_internal message, dir
siblings = Tree.siblings :include_label=>true
# Remove labels
- siblings = siblings.map{|i| Line.without_label(:line=>i)}
+
# Remove "untracked (ignore)"
siblings = self.remove_options siblings
unless siblings.any? # Error if no siblings
- return "@flash/- Provide some files (on lines next to this menu, with no blank lines, and no 'untracked' label!"
+ return "<* Provide some files (on lines next to this menu, with no blank lines, and no untracked files)!"
end
- Console.run "git commit -m \"#{message}\" #{siblings.join("\\\n ")}", :dir=>dir#, :no_enter=>true
+ siblings = siblings.map{|o| "\"#{o}\""}.join(" ")
+ siblings.gsub!(" -> ", "\" \"") # In case any "a -> b" exist, from renames
+
+ # Todo > only cd if necessary!
+
+ DiffLog.quit_and_run "cd \"#{dir}\"\ngit commit -m \"#{message}\" #{siblings}"
+
end
def self.unadd
- dir = Tree.closest_dir yield[:ancestors]
+
+ dir = options[:dir]
siblings = Tree.siblings :include_label=>true
siblings.map!{|i| Line.without_label(:line=>i)}
@@ -604,13 +692,14 @@ def self.unadd
return "- No files to unadd (they should be siblings of unadd)!" if siblings.empty? # Error if no siblings
command = "git reset #{siblings.join(' ')}"
- txt = Console.sync command, :dir=>dir
+ txt = Shell.sync command, :dir=>dir
return Tree.quote txt if txt.any?
- "@flash/- unadded!"
+ "<* unadded!"
end
def self.revert
- dir = Tree.closest_dir yield[:ancestors]
+
+ dir = options[:dir]
siblings = Tree.siblings :include_label=>true
siblings.map!{|i| Line.without_label(:line=>i)}
@@ -619,13 +708,14 @@ def self.revert
return "- No files to revert (they should be siblings of revert)!" if siblings.empty? # Error if no siblings
command = "git checkout #{siblings.join(' ')}"
- txt = Console.sync command, :dir=>dir
+ txt = Shell.sync command, :dir=>dir
return Tree.quote txt if txt.any?
- "@flash/- reverted!"
+ "<* reverted!"
end
- def self.delete
- dir = Tree.closest_dir yield[:ancestors]
+ def self.remove
+ options = yield
+ dir = options[:dir]
siblings = Tree.siblings :include_label=>true
siblings.map!{|i| Line.without_label(:line=>i)}
@@ -633,10 +723,10 @@ def self.delete
return "- No files to unadd (they should be siblings of delete)!" if siblings.empty? # Error if no siblings
- command = "rm #{siblings.join(' ')}"
- txt = Console.sync command, :dir=>dir
+ command = "git rm #{siblings.map{|o| "\"#{o}\""}.join(' ')}" # "
+ txt = Shell.sync command, :dir=>dir
return Tree.quote txt if txt.any?
- "@flash/- deleted!"
+ "<* deleted!"
end
def self.git_diff_options
@@ -653,32 +743,25 @@ def self.search_just_push
Search.isearch match
end
+ # Meant to search git diffs.
def self.search_repository
- # .code_tree_diff was deprecated in Unified refactor - see do+push
self.code_tree_diff
end
- def self.docs
- "
- > How to use
- | Put the @git menu under a path that is (or you want to make into)
- | a git repo, like so:
- |
- | /tmp/myproject/
- | @git/
- "
- end
-
def self.create
- dir = Tree.closest_dir yield[:ancestors]
- result = Console.sync 'git init', :dir=>dir
+
+ options = yield
+ dir = options[:dir]
+ result = Shell.sync 'git init', :dir=>dir
"
| #{result.strip}
+ |
+ | Now re-expand the \"git\" command.
"
end
def self.file_in_repository? dir, file
- txt = Console.sync "git ls-files '#{file}'", :dir=>dir
+ txt = Shell.sync "git ls-files '#{file}'", :dir=>dir
txt.any? # It's in the repository if it didn't return blank
end
diff --git a/menu/git_docs/index.menu b/roots/git_docs/index.menu
similarity index 100%
rename from menu/git_docs/index.menu
rename to roots/git_docs/index.menu
diff --git a/menu/git_docs/index.rb b/roots/git_docs/index.rb
similarity index 89%
rename from menu/git_docs/index.rb
rename to roots/git_docs/index.rb
index a411d217..4effc379 100644
--- a/menu/git_docs/index.rb
+++ b/roots/git_docs/index.rb
@@ -8,7 +8,7 @@ def self.menu_after output, *args
# Prepend"git-" (except for gitweb command)
command = "git-#{command}" if ! ["gitweb", "gitk"].member? command
- txt = Console.sync "man #{command}", :clean=>1
+ txt = Shell.sync "man #{command}", :clean=>1
txt.sub! /\n\n\n+/, "\n\n" # Remove first group of multiple linebreaks
diff --git a/roots/google.rb b/roots/google.rb
new file mode 100644
index 00000000..f89c0244
--- /dev/null
+++ b/roots/google.rb
@@ -0,0 +1,39 @@
+require "cgi"
+
+module Xiki::Menu
+ class Google
+ def self.menu *args
+
+ if args.empty? # If no path, pull from history
+ return " Type a search string"
+ end
+
+ # If multiple args, they were slash-delimited
+
+ txt = args.length > 1 ?
+ args.join("/") : args[0]
+
+ if txt =~ /\n/
+
+ # Multi-line, so convert to single line...
+
+ txt.gsub!("\n", " ")
+
+ elsif txt =~ /^:/
+ txt.sub! /^: /, ''
+
+ elsif Line !~ /google\//
+
+ # Words not quoted (and not single "google/foo" line) so grab siblings...
+
+ txt = Tree.siblings.join(" ")
+
+ end
+
+ Xiki::Google.search txt, :via_os=>(Keys.prefix_u ? nil : 1)
+
+ nil
+ end
+
+ end
+end
diff --git a/lib/xiki/tools/google_images.rb b/roots/google_images.rb
similarity index 57%
rename from lib/xiki/tools/google_images.rb
rename to roots/google_images.rb
index 2dc96083..9b7a4e59 100644
--- a/lib/xiki/tools/google_images.rb
+++ b/roots/google_images.rb
@@ -1,8 +1,10 @@
module Xiki
class GoogleImages
- def self.menu *args
- return View.prompt "Type something to google image search for" if args.blank?
- url = "https://www.google.com/search?tbm=isch&q=#{CGI.escape ENV['txt']}"
+ def self.menu *txt
+ return View.prompt "Type something to google image search for" if txt.blank?
+ txt = txt.join('/')
+ txt = CGI.escape txt
+ url = "https://www.google.com/search?tbm=isch&q=#{txt}"
# If up+..., just return url
return "@ #{url}" if Keys.up?
diff --git a/roots/haml.rb b/roots/haml.rb
new file mode 100644
index 00000000..7862fd1d
--- /dev/null
+++ b/roots/haml.rb
@@ -0,0 +1,34 @@
+module Xiki::Menu
+ class Haml
+ def self.menu *args
+
+ return "
+ > Type some HAML here to run it in your browser
+ | %h1 Example
+ | %table{style: 'width:100%;'}
+ | %tr
+ | %td Stuff in
+ | %td Table cells
+ " if args == []
+
+ txt = args[0]
+ return "=beg/quoted/" if txt !~ /\n/
+
+
+ File.open("/tmp/tmp.haml", "w") { |f| f << txt }
+ output = Shell.sync "haml /tmp/tmp.haml /tmp/tmp.html"
+
+ if output.any?
+ return Tree.quote output
+ end
+
+ File.open("/tmp/tmp.html", "a") { |f| f << Xiki::Html.default_css }
+
+ # Then load in browser (or reload)
+ Firefox.value('document.location.toString()') == "file:///tmp/tmp.html" ?
+ Firefox.reload :
+ $el.browse_url("file:///tmp/tmp.html")
+
+ "<* Loaded in browser!"
+ end
+end; end
diff --git a/roots/handlers.rb b/roots/handlers.rb
new file mode 100644
index 00000000..47438f14
--- /dev/null
+++ b/roots/handlers.rb
@@ -0,0 +1,51 @@
+module Xiki
+ class Handlers
+
+ def self.menu *args
+
+ # Grab parent subpath to parse and pre-expand
+
+ options = yield
+ ancestors = options[:ancestors]
+
+ raise ": Nest this under another menu.\n<< all handlers/" if ! ancestors
+
+ menu_steps = ancestors[-1] == "menu steps/"
+ ancestors.pop if menu_steps
+
+ path = ancestors.any? ? ancestors : options[:path]
+
+
+ options_result = Expander.expanders path
+
+ if options_result[:menufied]
+ Command.climb_sources options_result
+ end
+
+
+ options_result_orig = options_result.dup
+
+ Command.determine_handlers options_result
+
+ options_result_orig.keys.each{|k| options_result.delete k}
+
+ txt = self.ap options_result
+ txt.gsub! /^ /, ""
+ txt.sub! "[\n [", "[["
+ txt = Tree.quote txt, :char=>"|"
+
+ txt << "<< all handlers/\n"
+
+ txt
+
+ end
+
+ def self.ap txt
+ txt = txt.ai
+ txt.sub! /\A{\n/, ''
+ txt.sub! /\n}\z/, ''
+ txt
+ end
+
+ end
+end
diff --git a/roots/help.rb b/roots/help.rb
new file mode 100644
index 00000000..f0433a47
--- /dev/null
+++ b/roots/help.rb
@@ -0,0 +1,34 @@
+module Menu
+ class Help
+
+ def self.menu_after txt, *args
+ options = yield
+ options[:hotkey] = nil
+ txt
+ end
+
+ MENU = %`
+ | Welcome to xsh (Xiki Shell)!
+ |
+ | For the tutorial, type Ctrl+Q then "xsh --tutorial".
+ |
+ | For help with command line args, type Ctrl+Q to quit, then type
+ | "xsh --help" back in your normal shell. Or expand it right here
+ | (move your arrow keys to the following line and press Ctrl+O):
+ |
+ $ xsh --help
+ |
+ | Connecting with people
+ - chat room/
+ | Jump into the Xiki chat room to get help, chat about,
+ | Xiki or propose a command!
+ |
+ = http://webchat.freenode.net/?channels=xiki
+ - mailing list/
+ | Join the mailing list to get help via email.
+ |
+ = http://groups.google.com/group/xiki/
+
+ `
+ end
+end
diff --git a/roots/help_topics.menu b/roots/help_topics.menu
new file mode 100644
index 00000000..81f00b10
--- /dev/null
+++ b/roots/help_topics.menu
@@ -0,0 +1,9 @@
++ external shell keyboard shortcuts/
+ | See
+ = ~/.xsh
+
+| See
+<< help/
+
+|?Note: This menu is in progress, and is unfinished.
+| Edit xiki/roots/help_topics.menu to contribute!
diff --git a/roots/highlight.rb b/roots/highlight.rb
new file mode 100644
index 00000000..37fe5f0d
--- /dev/null
+++ b/roots/highlight.rb
@@ -0,0 +1,46 @@
+class Highlight
+ MENU = "
+ - .all/
+ - .show/
+ "
+
+ def self.all
+ hash = Xiki::Color.all_marks_hash
+
+ if hash.empty? # If no marks found, just say so
+ return "- no marks found!"
+ end
+
+ keys = hash.keys.sort.reverse
+
+ txt = ""
+ last_file = nil
+ keys.each do |key|
+ file, line = hash[key]
+ if last_file == file # If same file as last, just add line
+ txt << " : #{line}"
+ else # Else, show file and line
+ txt << "= #{file.sub /(.+\/)/, "\\1\n - "}\n : #{line}"
+ end
+
+ last_file = file
+ end
+
+ Tree.<< txt, :no_search=>1
+
+ # Apply colors...
+
+ keys.each do |key|
+ Move.to_quote
+ over = $el.make_overlay(Line.left+Line.indent.length, Line.right+1)
+ $el.overlay_put over, :face, hash[key]
+ end
+
+ Tree.to_parent :prefix=>:u
+ Move.to_quote
+
+ ""
+ end
+
+end
+
diff --git a/roots/history.rb b/roots/history.rb
new file mode 100644
index 00000000..4806ee9e
--- /dev/null
+++ b/roots/history.rb
@@ -0,0 +1,16 @@
+log_file = File.expand_path("~/.xiki/misc/logs/topic_log.xiki")
+
+# ~, so show options
+
+option_item = task
+
+return "* edit" if option_item == []
+if option_item == ["edit"]
+ return View.open log_file
+end
+
+
+# /, so list history
+
+txt = File.read log_file
+txt.split("\n").reverse.uniq.map{|o| "= #{o}"}.join("\n")
diff --git a/roots/homebrew.menu b/roots/homebrew.menu
new file mode 100644
index 00000000..43a54fe0
--- /dev/null
+++ b/roots/homebrew.menu
@@ -0,0 +1 @@
+<< brew/
diff --git a/roots/http.rb b/roots/http.rb
new file mode 100644
index 00000000..db7ecb26
--- /dev/null
+++ b/roots/http.rb
@@ -0,0 +1,28 @@
+# It's a url (1st args is blank), so delegate to http://... passing "~ source"...
+
+# Construct url
+url = "http:/#{args.join("/")}"
+
+Xiki[url, :task=>["source"]]
+
+
+# cla## Http
+# def self.menu *args
+# yield[:dont_log] = 1
+# url = args.blank? ? nil : args.join('/')
+#
+# # If as+open, just jump to the log
+# return View.open Launcher.log_file if Keys.open?
+#
+# # If no url's, list them all...
+#
+# if url.blank?
+# txt = Launcher.last "http", :exclude_path=>1
+# txt.gsub! /^- /, '<< '
+# return txt
+# end
+#
+# Keys.prefix == :u ? $el.browse_url(url) : Firefox.url(url)
+# nil
+# end
+# end
diff --git a/roots/i.xiki b/roots/i.xiki
new file mode 100644
index 00000000..d6c8b45b
--- /dev/null
+++ b/roots/i.xiki
@@ -0,0 +1 @@
+! View.layout_todo_and_nav
diff --git a/roots/ids.rb b/roots/ids.rb
new file mode 100644
index 00000000..55a1b3ca
--- /dev/null
+++ b/roots/ids.rb
@@ -0,0 +1,40 @@
+class Ids
+ def self.menu *args
+
+
+ options = yield
+
+Ol()
+
+ # If just "ids/", list all...
+
+ if args.blank?
+ js = %`
+ var result="";
+ $('*[id]').each(function(i, e){
+ var id = $(e).attr('id');
+ result += "+ #"+id+"/\\n";
+ })
+ return result;
+ `.unindent
+
+Ol.a js
+ result = Browser.js js
+ return "
+ > No id attributes found in page!
+ | Maybe try listing by classes:
+ << css/list/
+ " if result == '""'
+ return result
+ end
+
+ # Id passed so just navigate to it...
+
+Ol "args", args # => ["#hi", "new text\n"]
+ # => ["#hi"]
+
+ # return "todo"
+ Xiki["dom/", args, options]
+
+ end
+end
diff --git a/lib/xiki/tools/img.rb b/roots/img.rb
similarity index 63%
rename from lib/xiki/tools/img.rb
rename to roots/img.rb
index 8ce51c60..a754ec52 100644
--- a/lib/xiki/tools/img.rb
+++ b/roots/img.rb
@@ -1,6 +1,7 @@
module Xiki
class Img
def self.menu *args
+Ol "args", args
path = args.join('/')
@@ -8,9 +9,13 @@ def self.menu *args
Line.sub! /^([ +-]*).*/, "\\1"
Move.to_end
- at = column > 0 ? "@" : ""
+ at = column > 0 ? "=" : ""
+Ol()
Image.<< path, "#{at}img/#{path}"
- Move.to_column column
+Ol()
+Ol "column", column
+ Move.to_column column+1
+# Move.to_column column
nil
end
end
diff --git a/roots/includes.rb b/roots/includes.rb
new file mode 100644
index 00000000..6066e371
--- /dev/null
+++ b/roots/includes.rb
@@ -0,0 +1 @@
+Browser.js("$.makeArray( $('script[src]').map(function(i, o){return '@'+$(o).attr('src')}) ).join('\\n')")
diff --git a/roots/interactions.rb b/roots/interactions.rb
new file mode 100644
index 00000000..3a303ddb
--- /dev/null
+++ b/roots/interactions.rb
@@ -0,0 +1,35 @@
+dir = File.expand_path '~/.xiki/interactions'
+
+file = "#{dir}/#{(args[0]||"").gsub ' ', '_'}.xiki"
+
+if task == []
+ return "* save as command/"
+elsif task == ["save as command"]
+ options[:nest] = 1
+ options[:no_task] = 1
+ options[:line_found] = 2
+ return "| Give your command a name\nfoo"
+elsif task && task[0] == "save as command"
+ name = task[1].gsub('_', ' ')
+ txt = File.read file
+ commands_dir = File.expand_path("~/.xiki/roots/")
+ FileUtils.mkdir_p commands_dir
+ File.open("#{commands_dir}/#{name}.xiki", "w") { |f| f << txt }
+ return "<* - saved ~/.xiki/roots/#{name}.xiki"
+end
+
+# /, so read in dir, but order by date...
+
+if args == []
+
+ return ": task unimplemented yet...\n* new" if options[:task]
+
+ files = Dir.entries(dir).select{|o| o !~ /^\./}.sort_by{ |x| File.stat("#{dir}/#{x}").mtime }.reverse
+ return files.map{|o| "+ #{o.sub(/\.xiki$/, '').gsub('_', ' ')}\n"}.join
+end
+
+# /foo/:contents, so navigate to it...
+
+View.open file
+
+""
diff --git a/menu/ip.rb b/roots/ip.rb
similarity index 60%
rename from menu/ip.rb
rename to roots/ip.rb
index 737d4c69..adaec793 100644
--- a/menu/ip.rb
+++ b/roots/ip.rb
@@ -1,4 +1,6 @@
-# return "127.1.2.3" # Hard-coded IP, for when demoing
+# return "127.3.3.7" # Hard-coded IP, for when demoing
+
+return if args.any?
inet = `ifconfig`.split("\n").grep(/\binet\b/)
diff --git a/lib/xiki/tools/itunes.rb b/roots/itunes.rb
similarity index 64%
rename from lib/xiki/tools/itunes.rb
rename to roots/itunes.rb
index 21dc2648..522c23a7 100644
--- a/lib/xiki/tools/itunes.rb
+++ b/roots/itunes.rb
@@ -1,7 +1,6 @@
module Xiki
class Itunes
- def self.menu
- "
+ MENU = "
- .play/
- .pause/
- .next/
@@ -9,43 +8,77 @@ def self.menu
- .artists/
- .songs/
- .current/
- - .playlist/
+ - .playlists/
- api/
> Play a song
- @ Itunes.songs 'Thunderstorm'
+ =Itunes.songs 'Thunderstorm'
|
- docs/
| Play specific song
- @itunes/songs/Around the Fur
+ =itunes/songs/Around the Fur
|
| Play a playlist
- @itunes/playlist/class
+ =itunes/playlist/class
+ << itunes store/
"
- end
@@use_pipe_delimiter = "set Applescript's text item delimiters to \"|\""
+ def self.menu_after output, *args
+ return if output # MENU handled it, so don't interfere
+
+ # /:song..., so play it
+
+ if args[0] =~ /: (.+)/
+ song = $1
+ self.songs song
+ end
+
+ end
+
def self.songs name=nil
- # If nothing passed, list all songs
+
+ # /, so list all songs...
+
if name.nil?
tracks = Applescript.run "get the name of every track of library playlist 1 as string", :app=>"iTunes", :delimiter=>"|"
tracks.sub! /^\"(.+)\"$/, "\\1"
tracks = tracks.split("|")
- return tracks.sort.uniq.select{|o| o != "" && o !~ /^ /}.map{|o| "- #{o}/\n"}.join
+ return tracks.sort.uniq.select{|o| o != "" && o !~ /^ /}.map{|o| ": #{o}\n"}.join
return
end
+ # /song, so play it...
+
+ name.sub!(/^: /, '')
+
Applescript.run "iTunes", "play track \"#{name}\""
+ "<*"
end
def self.current
- Applescript.run "iTunes", "get name of current track"
+ txt = Applescript.run "iTunes", "get name of current track"
+ "- #{txt.gsub "\"", ''}/"
end
- def self.playlist name=nil
- return View.prompt("Enter a name") if name.nil?
+ def self.playlists name=nil
+
+ # /, so show playlists...
+
+ if name.nil?
+ tracks = Applescript.run "get name of every playlist as string", :app=>"iTunes", :delimiter=>"|"
+ tracks.sub! /^\"(.+)\"$/, "\\1"
+ tracks = tracks.split("|")
+ return tracks.map{|o| ": #{o}\n"}.join
+ end
+
+ # /name, so play it...
+
+ name.sub! /^: /, ''
+
Applescript.run "iTunes", "play playlist \"#{name}\""
+ "<*"
end
def self.play
@@ -57,7 +90,7 @@ def self.pause
def self.next
Applescript.run "iTunes", "next track"
- ".flash - #{self.current}"
+ "<* #{self.current.gsub('\"', '')}"
end
def self.previous
Applescript.run "iTunes", "previous track"
@@ -81,7 +114,7 @@ def self.artists artist=nil, track=nil
end
self.songs track
- ".flash - Playing!"
+ "<* Playing!"
end
end
end
diff --git a/roots/javascript.rb b/roots/javascript.rb
new file mode 100644
index 00000000..cffbced5
--- /dev/null
+++ b/roots/javascript.rb
@@ -0,0 +1,16 @@
+# /, so show message...
+
+if args == []
+ return "
+ | // Type some javascript here (to evaluate)
+ | return 1 + 2
+ "
+end
+
+# /code, so eval it...
+
+txt = args[0]
+
+txt = "print = p = console.log;\n#{txt}"
+
+Tree.quote JavascriptHandler.eval(txt)
diff --git a/roots/jekyll.menu b/roots/jekyll.menu
new file mode 100644
index 00000000..a1252bc5
--- /dev/null
+++ b/roots/jekyll.menu
@@ -0,0 +1,26 @@
+- create/
+ @/tmp/
+ $ jekyll new myblog
+- view/
+ - localhost/
+ @/tmp/myblog/
+ % jekyll serve -w
+ @http://localhost:4000/
+ - files/
+ | This is an alternative to starting the server, but the relative
+ | links won't work when viewing the files directly.
+ @/tmp/myblog/
+ $ jekyll build
+ @file:///tmp/myblog/_site/index.html
+- new post/
+ ! %`
+ ! @/tmp/myblog/
+ ! - _posts/
+ ! - {date}-hi-again.markdown
+ ! | ---
+ ! | title: "Hi Again"
+ ! | date: {date}
+ ! | layout: post
+ ! | ---
+ ! | This page is simple.
+ ! `.gsub("{date}", Time.now.strftime("%Y-%m-%d"))
diff --git a/roots/js.rb b/roots/js.rb
new file mode 100644
index 00000000..be77f2c7
--- /dev/null
+++ b/roots/js.rb
@@ -0,0 +1,35 @@
+# /, so show sample...
+
+return "| $('h1').toggle(1000) // Type some javascript here (to run in the browser)" if args == []
+
+# /~, so handle options...
+
+return "* show output" if task == []
+
+txt = args[-1]
+txt.sub! /\A: /, ''
+
+# Prepend assigning it to a variable, so it can be run again
+txt = "window.xl = function(){ eval(#{txt.inspect}) };
+
+#{txt}"
+
+if task == ["show output"] || txt =~ /^return /
+
+ txt = "\n#{txt.strip}"
+ txt.sub!(/.*\n/m, "\\0return ") if txt !~ /^ *return\b/
+ txt << "\n"
+ txt.sub!(/\A\n/, "")
+
+ result = Browser.js txt
+
+ return Tree.pipe result
+end
+
+
+n = Keys.prefix_n || 1
+n.times do
+ Browser.js txt
+end
+
+nil
diff --git a/roots/jso.rb b/roots/jso.rb
new file mode 100644
index 00000000..342328c4
--- /dev/null
+++ b/roots/jso.rb
@@ -0,0 +1,22 @@
+
+return "
+ - Old > use instead:
+ = js
+ | return 1 + 2
+"
+
+
+
+Applescript.exec("Firefox", "activate") if Keys.prefix_u
+
+return "
+ > Type a js expression here (to grab from the browser)
+ | document.title
+ " if args.blank?
+
+txt = args[0]
+return "=beg/quoted/" if txt !~ /\n/
+
+txt = args[0]
+
+Tree.quote Firefox.exec txt
diff --git a/roots/jump_to_dir.rb b/roots/jump_to_dir.rb
new file mode 100644
index 00000000..94f22d27
--- /dev/null
+++ b/roots/jump_to_dir.rb
@@ -0,0 +1,8 @@
+# /, so list all dirs
+
+links = Notes.extract_links :limit=>2000
+
+links.map!{|o| "= #{File.dirname o[0]}/\n"}
+links.uniq!
+links.join
+
diff --git a/roots/keys.rb b/roots/keys.rb
new file mode 100644
index 00000000..2c70208d
--- /dev/null
+++ b/roots/keys.rb
@@ -0,0 +1,2 @@
+# The "keys" command. Just a thin wrapper around Keys.menu_expander.
+txt = Keys.menu_expander args, options
diff --git a/roots/kill.rb b/roots/kill.rb
new file mode 100644
index 00000000..52957e9d
--- /dev/null
+++ b/roots/kill.rb
@@ -0,0 +1,107 @@
+class Kill
+ # Called like this:
+ # @kill/
+ # @kill/2.6 625 craig /Appli...
+ # @kill/xiki/2.6 625 craig /Appli...
+
+ MENU = %`
+ docs/
+ | This menu lists process, so you can expand to kill them.
+ - filter/
+ | If you pass an item it filters which processes are displayed. Example:
+ @kill/user/
+ - keys/
+ | The up key prefix will do a "kill -9", which is stronger
+ | than just a regular "kill".
+ << ports/
+ `
+
+ def self.menu_after output, *args
+Ol()
+
+ return if args.any? && output # If MENU sub-item that menu handled do nothing
+
+ # Grab filter and process args
+ filter = args.shift if args[0] !~ /^:/ # If 1st arg isn't pipe-quoted, it's a filter
+ process = args[0]
+
+ # /, so list processes, then MENU contents...
+
+ if ! process
+ txt = `ps -eo pcpu,pid,user,args | sort -k 1 -r`
+ header = txt.slice!(/^ *%.+\n/)
+ txt = "#{header}#{txt}"
+ txt.gsub! /^/, ': '
+
+ if filter
+ # txt = txt.split("\n").grep(/#{Regexp.quote filter}/i).join("\n")
+ txt = txt.split("\n").grep(/#{filter}/i).join("\n")
+ end
+
+ return "| none found\n#{output}" if txt == ""
+
+ txt.gsub!(/ +$/, '')
+ return "> Expand a process to kill it\n#{txt}\n#{output}"
+ end
+
+ # /process, so kill it...
+
+ options = yield
+
+ # right click, so show options...
+
+ return "
+ * kill
+ * sudo kill
+ * force kill
+ * kill listed
+ " if options[:task] == []
+
+ # Should just continue on
+ # return "<* killed" if options[:task] == ["kill"]
+
+ pid = process.split(/ +/)[2]
+ command = "kill #{pid}"
+ command = "kill -9 #{pid}" if options[:prefix] == :u || options[:task] == ["force kill"]
+
+ if options[:task] == ["kill listed"]
+ siblings = Tree.siblings
+ ids = siblings.map{|o| o.scan(/[\d.]+/)[1]}
+ command = "kill #{ids.join(' ')}"
+ end
+
+ # Dash+, so do sudo...
+
+ # if options[:task] == ["sudo kill"]
+ # Console
+ # return "<* fu"
+ # end
+
+ if options[:task] == ["sudo kill"] || options[:prefix] == :-
+ command = "sudo #{command}"
+ Shell.async command
+ return
+ end
+
+ # if options[:task] == ["force kill"]
+ # command = "kill -9 #{command}"
+ # Shell.async command
+ # return
+ # end
+
+ output = `#{command}`
+ View.flash "- sent kill!", :times=>1
+
+ if options[:task] == ["kill listed"]
+ Line.previous
+ Tree.delete_siblings
+ return ""
+ end
+
+ column = View.column
+ Line.delete
+ View.column = column
+
+ nil
+ end
+end
diff --git a/roots/l.menu b/roots/l.menu
new file mode 100644
index 00000000..d9378375
--- /dev/null
+++ b/roots/l.menu
@@ -0,0 +1 @@
+<< links/
diff --git a/menu/last.rb b/roots/last.rb
similarity index 100%
rename from menu/last.rb
rename to roots/last.rb
diff --git a/roots/length.rb b/roots/length.rb
new file mode 100644
index 00000000..d302cf07
--- /dev/null
+++ b/roots/length.rb
@@ -0,0 +1,6 @@
+return "=prompt/type a jquery selector here" if args.empty?
+
+selector = args.join "/"
+
+selector = "$(\"#{selector}\").length"
+Firefox.exec selector
diff --git a/roots/links.rb b/roots/links.rb
new file mode 100644
index 00000000..d4fb597b
--- /dev/null
+++ b/roots/links.rb
@@ -0,0 +1,125 @@
+extract_options = {}
+
+# links/methods/, so only extract methods
+if args[0] =~ /^[a-z]+$/
+
+ extract_options[:filter] = {
+ "methods"=>/^[ :]*def /,
+ "routes"=>/^[ :]*((get|post)|# Route) /,
+ "comments"=>/^[ :]*(#|\/\/|;+) /,
+ "outlog"=>/^[ :]*Ol[. ]/,
+ }[args[0]]
+
+ args.shift
+
+end
+
+
+# links/, so only extract all links
+
+links = Notes.extract_links extract_options
+
+# /, so show all links...
+
+if args == []
+ result, last_file = "", nil
+ links.each do |o|
+ if o[0] =~ /^>/
+ next result << "#{o[0]}\n"
+ end
+
+ result << "+ #{o[0].sub(/.+\//, '')}\n" if o[0] != last_file
+ result << " #{o[1]}\n"
+ last_file = o[0]
+ end
+ options[:filter_dont_collapse] = 1
+ options[:filter_number_counts_only_quotes] = 1
+ return result
+end
+
+# /file/:quote, so navigate to it...
+
+target_file = args.slice! 0
+target_quote = args[-1]
+# target_file: "notes.rb"
+# target_quote: ": def self.foo"
+
+return "* show in links\n* to top\n* lower\n* backwards\n* forwards\n* delete" if task == []
+
+if task == ["show in links"]
+ Links.show_in_nav target_file, target_quote
+ return ""
+
+elsif task && task.any?
+
+ # Show it in the nav, and go up to heading
+
+ Links.show_in_nav target_file, target_quote
+ Notes.to_block :up=>1
+ modified = View.modified? # Remember if nav was modified
+
+ if task == ["to top"]
+
+ # Move heading to the top
+ Notes.move_block_to_top :no_fade=>1
+
+ elsif task == ["lower"]
+
+ # Move heading down > to a few lower
+ Notes.move_block :prefix=>4
+
+ elsif task == ["backwards"] || task == ["forwards"]
+
+ backwards = task == ["backwards"]
+
+ # Move up to heading
+ # Move it up or down
+ backwards ? Notes.move_block(:up=>1) : Notes.move_block
+ # Notes.move_block backwards ? :backwards : nil
+
+ elsif task == ["delete"]
+ # Move up to heading
+
+ Notes.cut_block :no_clipboard
+
+ end
+
+ # Save changes and redisplay (unless it was modified)...
+
+ if ! modified
+ DiffLog.save :no_diffs=>1
+ Launcher.open("links/")
+ end
+
+ return ""
+
+
+end
+
+
+# Iterate through args until we find quote and path file stem...
+links.each do |file, quote|
+ next if file !~ /#{Regexp.quote target_file}$/ # => nil
+ next if target_quote && quote.strip.sub(/^- /, '') != target_quote.strip
+
+ # - foo, so jump to it in ^links...
+
+ # if target_quote =~ /^\w.*:$/
+ if target_quote && target_quote !~ /^:/
+ View.open "%links"
+ View.to_top
+ Search.forward $el.regexp_quote(target_quote)
+ Line.to_words
+ #View.to_quote target_quote.sub(/^: /, '')
+ break
+ end
+
+ View.open file
+
+ if target_quote =~ /^:/ # : Foo, so jump to it in file
+ View.to_snippet target_quote.sub(/^: /, '')
+ end
+ break
+end
+
+""
diff --git a/lib/xiki/tools/ln.menu b/roots/ln.menu
similarity index 100%
rename from lib/xiki/tools/ln.menu
rename to roots/ln.menu
diff --git a/roots/log.rb b/roots/log.rb
new file mode 100644
index 00000000..d1223f1d
--- /dev/null
+++ b/roots/log.rb
@@ -0,0 +1 @@
+Launcher.log
diff --git a/roots/lorem_ipsum.menu b/roots/lorem_ipsum.menu
new file mode 100644
index 00000000..0b023186
--- /dev/null
+++ b/roots/lorem_ipsum.menu
@@ -0,0 +1,11 @@
+- all/
+ | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+- individual sentences/
+ - Lorem ipsum.../
+ | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ - Ut enim.../
+ | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+ - Duis aute.../
+ | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
+ - Excepteur sint/
+ | Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/menu/ls.rb b/roots/ls.rb
similarity index 51%
rename from menu/ls.rb
rename to roots/ls.rb
index c59cf284..7b18d79f 100644
--- a/menu/ls.rb
+++ b/roots/ls.rb
@@ -11,9 +11,15 @@ def self.docs
end
def self.menu *args
+ options = yield
+ dir = options[:dir]
+
return self.docs if args == ['docs']
- path = Tree.file :require=>1
- Tree.quote Console.sync "ls -lah \"#{path}\"", :dir=>path
+ # path = Tree.file :require=>1
+ txt = Shell.sync "ls -Alh", :dir=>dir
+ # txt = Shell.sync "ls -Alh \"#{path}\"", :dir=>dir
+ txt.sub! /\Atotal.+\n/, ''
+ Tree.quote txt
end
end
diff --git a/roots/mail/default.conf b/roots/mail/default.conf
new file mode 100644
index 00000000..8e204532
--- /dev/null
+++ b/roots/mail/default.conf
@@ -0,0 +1,10 @@
+- from/
+ foo@bar.com
+- client id/
+ 7777777-samplesamplesamplesample.apps.googleusercontent.com
+- client secret/
+ tNsamplesamplesamplesample_E5
+- access token/
+ ya29.1.samplesamplesamplesamplesamplesamplesamplesample-samplesample
+- refresh token/
+ 1/samplesamplesamplesamplesamplesample
diff --git a/roots/mail/mail_index.rb b/roots/mail/mail_index.rb
new file mode 100644
index 00000000..f448c847
--- /dev/null
+++ b/roots/mail/mail_index.rb
@@ -0,0 +1,128 @@
+module Menu
+ class Mail
+ def self.menu *args
+ options = yield
+
+ # /, so show list contacts in =email addresses...
+
+ if args == []
+ txt = Xiki["`email addresses"].gsub(/^> (.+)/, "- \\1/")
+ txt << "\n=email addresses/\n=conf/\n"
+ txt << "| Make this use =contacts instead\n=contacts/"
+ return txt
+ end
+
+ # /name or /email, so show sample message...
+
+ if args.length == 1
+ return "
+ > The subject
+ | The body of
+ | the email.
+ "
+ end
+
+ email = args[0]
+
+ if email !~ /@.+\./ # if not @, get the email address
+ email = Xiki["`email addresses/> #{email}"]
+ email = Tree.unquote(email).strip
+ end
+
+ # Read in conf, and complain if not set up...
+
+ conf = options[:conf]
+ return "
+ > Set conf values first
+ =conf/mail/
+ " if conf.strip == ""
+
+ # Send the email...
+
+ conf = Xik.new conf
+ from_address = conf["from"]
+
+ require 'net/smtp'
+ require 'mail'
+ require 'gmail_xoauth'
+
+ the_subject = Tree.siblings[0].sub(/^> /, '')
+ txt = args[1]
+
+ # Remove linebreaks on lines ending in trailing spaces
+
+ txt.gsub! " \n", " "
+
+ raise "- Expand one of the |... lines!" if txt !~ /\n/
+
+ mail = ::Mail.new do
+ from from_address
+ to email
+ subject the_subject
+ body txt # add an attachment via add_file
+ end
+
+ begin
+ smtp = Net::SMTP.new('smtp.gmail.com', 587)
+ smtp.enable_starttls_auto
+ smtp.start('gmail.com', from_address, conf["access token"], :xoauth2) do |smtp|
+ smtp.send_message mail.to_s, from_address, email
+ end
+ rescue Exception=>e
+
+ # If not auth error, error out
+
+ if e.message !~ /\A334/
+ return "
+ > Todo > find pattern
+ | continue on when it looks like the token needs refreshing
+ > Error
+ | #{e.message}
+ "
+ end
+
+ # If a "refesh token" in conf already, try to regenerate token ourself.
+
+ if (refresh_token = conf["refresh token"]) && refresh_token != "1/samplesamplesamplesamplesamplesample"
+
+ View.flash "- generating new token from refresh_token!"
+
+ txt = Shell.sync "python oauth2.py --client_id=#{conf['client id']} --client_secret=#{conf['client secret']} --refresh_token=#{refresh_token}",
+ :dir=>"#{Xiki.dir}roots/mail/"
+
+ Ol.a txt
+ access_token = txt[/Access Token: (.+)/, 1]
+
+
+ conf_file = File.expand_path "~/.xiki/roots/conf/mail.conf"
+ conf_txt = File.read conf_file
+ conf_txt.sub! /^(- access token\/\n ).+/, "\\1#{access_token}"
+ File.open(conf_file, "w") { |f| f << conf_txt }
+
+ # TODO: call service again? > Or just make them refresh each time?
+
+
+ return "
+ - generated new token from refresh_token!
+ | Expand again to send
+ "
+ end
+
+ return "
+ > Oauth access token needs to be updated
+ | 1. Run this command:
+ =#{Xiki.dir}roots/mail/
+ % python oauth2.py --generate_oauth2_token --client_id=#{conf['client id']} --client_secret=#{conf['client secret']}
+ | 2. Save it in the conf:
+ =conf/mail/
+ "
+
+ end
+
+ # "<* sent!"
+ "- sent"
+
+ end
+ end
+end
+
diff --git a/roots/mail/oauth2.py b/roots/mail/oauth2.py
new file mode 100644
index 00000000..89bdc9cd
--- /dev/null
+++ b/roots/mail/oauth2.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+#
+# Copyright 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+ # http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Performs client tasks for testing IMAP OAuth2 authentication.
+
+To use this script, you'll need to have registered with Google as an OAuth
+application and obtained an OAuth client ID and client secret.
+See http://code.google.com/apis/accounts/docs/OAuth2.html for instructions on
+registering and for documentation of the APIs invoked by this code.
+
+This script has 3 modes of operation.
+
+1. The first mode is used to generate and authorize an OAuth2 token, the
+first step in logging in via OAuth2.
+
+ oauth2 --user=xxx@gmail.com \
+ --client_id=1038[...].apps.googleusercontent.com \
+ --client_secret=VWFn8LIKAMC-MsjBMhJeOplZ \
+ --generate_oauth2_token
+
+The script will converse with Google and generate an oauth request
+token, then present you with a URL you should visit in your browser to
+authorize the token. Once you get the verification code from the Google
+website, enter it into the script to get your OAuth access token. The output
+from this command will contain the access token, a refresh token, and some
+metadata about the tokens. The access token can be used until it expires, and
+the refresh token lasts indefinitely, so you should record these values for
+reuse.
+
+2. The script will generate new access tokens using a refresh token.
+
+ oauth2 --user=xxx@gmail.com \
+ --client_id=1038[...].apps.googleusercontent.com \
+ --client_secret=VWFn8LIKAMC-MsjBMhJeOplZ \
+ --refresh_token=1/Yzm6MRy4q1xi7Dx2DuWXNgT6s37OrP_DW_IoyTum4YA
+
+3. The script will generate an OAuth2 string that can be fed
+directly to IMAP or SMTP. This is triggered with the --generate_oauth2_string
+option.
+
+ oauth2 --generate_oauth2_string --user=xxx@gmail.com \
+ --access_token=ya29.AGy[...]ezLg
+
+The output of this mode will be a base64-encoded string. To use it, connect to a
+IMAPFE and pass it as the second argument to the AUTHENTICATE command.
+
+ a AUTHENTICATE XOAUTH2 a9sha9sfs[...]9dfja929dk==
+"""
+
+import base64
+import imaplib
+import json
+from optparse import OptionParser
+import smtplib
+import sys
+import urllib
+
+
+def SetupOptionParser():
+ # Usage message is the module's docstring.
+ parser = OptionParser(usage=__doc__)
+ parser.add_option('--generate_oauth2_token',
+ action='store_true',
+ dest='generate_oauth2_token',
+ help='generates an OAuth2 token for testing')
+ parser.add_option('--generate_oauth2_string',
+ action='store_true',
+ dest='generate_oauth2_string',
+ help='generates an initial client response string for '
+ 'OAuth2')
+ parser.add_option('--client_id',
+ default=None,
+ help='Client ID of the application that is authenticating. '
+ 'See OAuth2 documentation for details.')
+ parser.add_option('--client_secret',
+ default=None,
+ help='Client secret of the application that is '
+ 'authenticating. See OAuth2 documentation for '
+ 'details.')
+ parser.add_option('--access_token',
+ default=None,
+ help='OAuth2 access token')
+ parser.add_option('--refresh_token',
+ default=None,
+ help='OAuth2 refresh token')
+ parser.add_option('--scope',
+ default='https://mail.google.com/',
+ help='scope for the access token. Multiple scopes can be '
+ 'listed separated by spaces with the whole argument '
+ 'quoted.')
+ parser.add_option('--test_imap_authentication',
+ action='store_true',
+ dest='test_imap_authentication',
+ help='attempts to authenticate to IMAP')
+ parser.add_option('--test_smtp_authentication',
+ action='store_true',
+ dest='test_smtp_authentication',
+ help='attempts to authenticate to SMTP')
+ parser.add_option('--user',
+ default=None,
+ help='email address of user whose account is being '
+ 'accessed')
+ return parser
+
+
+# The URL root for accessing Google Accounts.
+GOOGLE_ACCOUNTS_BASE_URL = 'https://accounts.google.com'
+
+
+# Hardcoded dummy redirect URI for non-web apps.
+REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
+
+
+def AccountsUrl(command):
+ """Generates the Google Accounts URL.
+
+ Args:
+ command: The command to execute.
+
+ Returns:
+ A URL for the given command.
+ """
+ return '%s/%s' % (GOOGLE_ACCOUNTS_BASE_URL, command)
+
+
+def UrlEscape(text):
+ # See OAUTH 5.1 for a definition of which characters need to be escaped.
+ return urllib.quote(text, safe='~-._')
+
+
+def UrlUnescape(text):
+ # See OAUTH 5.1 for a definition of which characters need to be escaped.
+ return urllib.unquote(text)
+
+
+def FormatUrlParams(params):
+ """Formats parameters into a URL query string.
+
+ Args:
+ params: A key-value map.
+
+ Returns:
+ A URL query string version of the given parameters.
+ """
+ param_fragments = []
+ for param in sorted(params.iteritems(), key=lambda x: x[0]):
+ param_fragments.append('%s=%s' % (param[0], UrlEscape(param[1])))
+ return '&'.join(param_fragments)
+
+
+def GeneratePermissionUrl(client_id, scope='https://mail.google.com/'):
+ """Generates the URL for authorizing access.
+
+ This uses the "OAuth2 for Installed Applications" flow described at
+ https://developers.google.com/accounts/docs/OAuth2InstalledApp
+
+ Args:
+ client_id: Client ID obtained by registering your app.
+ scope: scope for access token, e.g. 'https://mail.google.com'
+ Returns:
+ A URL that the user should visit in their browser.
+ """
+ params = {}
+ params['client_id'] = client_id
+ params['redirect_uri'] = REDIRECT_URI
+ params['scope'] = scope
+ params['response_type'] = 'code'
+ return '%s?%s' % (AccountsUrl('o/oauth2/auth'),
+ FormatUrlParams(params))
+
+
+def AuthorizeTokens(client_id, client_secret, authorization_code):
+ """Obtains OAuth access token and refresh token.
+
+ This uses the application portion of the "OAuth2 for Installed Applications"
+ flow at https://developers.google.com/accounts/docs/OAuth2InstalledApp#handlingtheresponse
+
+ Args:
+ client_id: Client ID obtained by registering your app.
+ client_secret: Client secret obtained by registering your app.
+ authorization_code: code generated by Google Accounts after user grants
+ permission.
+ Returns:
+ The decoded response from the Google Accounts server, as a dict. Expected
+ fields include 'access_token', 'expires_in', and 'refresh_token'.
+ """
+ params = {}
+ params['client_id'] = client_id
+ params['client_secret'] = client_secret
+ params['code'] = authorization_code
+ params['redirect_uri'] = REDIRECT_URI
+ params['grant_type'] = 'authorization_code'
+ request_url = AccountsUrl('o/oauth2/token')
+
+ response = urllib.urlopen(request_url, urllib.urlencode(params)).read()
+ return json.loads(response)
+
+
+def RefreshToken(client_id, client_secret, refresh_token):
+ """Obtains a new token given a refresh token.
+
+ See https://developers.google.com/accounts/docs/OAuth2InstalledApp#refresh
+
+ Args:
+ client_id: Client ID obtained by registering your app.
+ client_secret: Client secret obtained by registering your app.
+ refresh_token: A previously-obtained refresh token.
+ Returns:
+ The decoded response from the Google Accounts server, as a dict. Expected
+ fields include 'access_token', 'expires_in', and 'refresh_token'.
+ """
+ params = {}
+ params['client_id'] = client_id
+ params['client_secret'] = client_secret
+ params['refresh_token'] = refresh_token
+ params['grant_type'] = 'refresh_token'
+ request_url = AccountsUrl('o/oauth2/token')
+
+ response = urllib.urlopen(request_url, urllib.urlencode(params)).read()
+ return json.loads(response)
+
+
+def GenerateOAuth2String(username, access_token, base64_encode=True):
+ """Generates an IMAP OAuth2 authentication string.
+
+ See https://developers.google.com/google-apps/gmail/oauth2_overview
+
+ Args:
+ username: the username (email address) of the account to authenticate
+ access_token: An OAuth2 access token.
+ base64_encode: Whether to base64-encode the output.
+
+ Returns:
+ The SASL argument for the OAuth2 mechanism.
+ """
+ auth_string = 'user=%s\1auth=Bearer %s\1\1' % (username, access_token)
+ if base64_encode:
+ auth_string = base64.b64encode(auth_string)
+ return auth_string
+
+
+def TestImapAuthentication(user, auth_string):
+ """Authenticates to IMAP with the given auth_string.
+
+ Prints a debug trace of the attempted IMAP connection.
+
+ Args:
+ user: The Gmail username (full email address)
+ auth_string: A valid OAuth2 string, as returned by GenerateOAuth2String.
+ Must not be base64-encoded, since imaplib does its own base64-encoding.
+ """
+ print
+ imap_conn = imaplib.IMAP4_SSL('imap.gmail.com')
+ imap_conn.debug = 4
+ imap_conn.authenticate('XOAUTH2', lambda x: auth_string)
+ imap_conn.select('INBOX')
+
+
+def TestSmtpAuthentication(user, auth_string):
+ """Authenticates to SMTP with the given auth_string.
+
+ Args:
+ user: The Gmail username (full email address)
+ auth_string: A valid OAuth2 string, not base64-encoded, as returned by
+ GenerateOAuth2String.
+ """
+ print
+ smtp_conn = smtplib.SMTP('smtp.gmail.com', 587)
+ smtp_conn.set_debuglevel(True)
+ smtp_conn.ehlo('test')
+ smtp_conn.starttls()
+ smtp_conn.docmd('AUTH', 'XOAUTH2 ' + base64.b64encode(auth_string))
+
+
+def RequireOptions(options, *args):
+ missing = [arg for arg in args if getattr(options, arg) is None]
+ if missing:
+ print 'Missing options: %s' % ' '.join(missing)
+ sys.exit(-1)
+
+
+def main(argv):
+ options_parser = SetupOptionParser()
+ (options, args) = options_parser.parse_args()
+ if options.refresh_token:
+ RequireOptions(options, 'client_id', 'client_secret')
+ response = RefreshToken(options.client_id, options.client_secret,
+ options.refresh_token)
+ print 'Access Token: %s' % response['access_token']
+ print 'Access Token Expiration Seconds: %s' % response['expires_in']
+ elif options.generate_oauth2_string:
+ RequireOptions(options, 'user', 'access_token')
+ print ('OAuth2 argument:\n' +
+ GenerateOAuth2String(options.user, options.access_token))
+ elif options.generate_oauth2_token:
+ RequireOptions(options, 'client_id', 'client_secret')
+ print 'To authorize token, visit this url and follow the directions:'
+ print ' %s' % GeneratePermissionUrl(options.client_id, options.scope)
+ authorization_code = raw_input('Enter verification code: ')
+ response = AuthorizeTokens(options.client_id, options.client_secret,
+ authorization_code)
+ print 'Refresh Token: %s' % response['refresh_token']
+ print 'Access Token: %s' % response['access_token']
+ print 'Access Token Expiration Seconds: %s' % response['expires_in']
+ elif options.test_imap_authentication:
+ RequireOptions(options, 'user', 'access_token')
+ TestImapAuthentication(options.user,
+ GenerateOAuth2String(options.user, options.access_token,
+ base64_encode=False))
+ elif options.test_smtp_authentication:
+ RequireOptions(options, 'user', 'access_token')
+ TestSmtpAuthentication(options.user,
+ GenerateOAuth2String(options.user, options.access_token,
+ base64_encode=False))
+ else:
+ options_parser.print_help()
+ print 'Nothing to do, exiting.'
+ return
+
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/menu/man.rb b/roots/man.rb
similarity index 100%
rename from menu/man.rb
rename to roots/man.rb
diff --git a/roots/map.rb b/roots/map.rb
new file mode 100644
index 00000000..ecf9391d
--- /dev/null
+++ b/roots/map.rb
@@ -0,0 +1,28 @@
+# If no arg, prompt to type something...
+
+txt = args.join(" ")
+return "=prompt/Type something to search on google maps" if ! txt.any?
+
+# If arg, look it up...
+
+txt.sub!(/ \(.+\)/, '') # Remove paren groups, like (16th street)
+
+txt = CGI.escape txt
+
+url = "http://maps.google.com/maps?q=#{txt.gsub "\n", ", "}"
+
+# Task to just show the url
+
+if task = options[:task]
+ return "* url" if task == []
+ options[:no_slash] = 1
+ return "= #{url}"
+end
+
+
+# /open (as+open), so just show url (google fucks it up in the browser)...
+return "= #{url}" if options[:prefix] == "open"
+
+Browser.url url, :os_open=>1
+
+nil
diff --git a/menu/markdown.rb b/roots/markdown.rb
similarity index 92%
rename from menu/markdown.rb
rename to roots/markdown.rb
index 2c12d64d..3f6008af 100644
--- a/menu/markdown.rb
+++ b/roots/markdown.rb
@@ -2,11 +2,15 @@
require 'redcarpet'
class Markdown
+ # include Xiki
def self.render txt
+ require "#{Xiki.dir}roots/html"
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink=>true, :space_after_headers=>true)
html = markdown.render txt
- html << Html.default_css
+
+ html << Xiki::Html.default_css
html
end
diff --git a/roots/markers.rb b/roots/markers.rb
new file mode 100644
index 00000000..53df34c3
--- /dev/null
+++ b/roots/markers.rb
@@ -0,0 +1,29 @@
+# /, so list labels from current file
+
+file = options[:dir].sub(/\/$/, '')
+
+if args == []
+ labels = Notes.extract_markers :file=>file, :limit=>100, :indent=>" "
+ labels = labels.split("\n", -1).uniq.join("\n")
+ options[:omit_slashes] = 1
+ return labels
+end
+
+# ~ option, so maybe navigate
+
+return "* navigate\n* run" if task == []
+
+
+# /label, so open the file and navigate to it
+
+View.open file
+View.to_top
+Notes.jump_to_label args[0]
+Line.next
+
+return "" if task == ["navigate"]
+
+Tree.collapse
+Launcher.launch
+
+""
diff --git a/roots/memorize.rb b/roots/memorize.rb
new file mode 100644
index 00000000..7779bb21
--- /dev/null
+++ b/roots/memorize.rb
@@ -0,0 +1,407 @@
+require 'uri'
+
+class Memorize
+
+ include Xiki # To have View class, etc.
+
+ attr :completed, :choices, :progress, :pending, :indent
+
+ def self.menu *args
+
+ options = yield
+ task = options[:task]
+
+ # /, so show example...
+
+ return "
+ > Expand this example to start memorizing
+ | France : Paris
+ | England : London
+ | Japan : Tokyo
+ | Germany : Berlin
+ " if args == []
+
+ raise "memorize command > didn't expect more than one item" if args.length != 1
+
+ # /text\n, so start memorizing...
+ if args[0] =~ /\n/ || args[0] =~ /\A>/
+
+ # /~, so offer to save to memorize.com...
+
+ return "
+ * learn on Memorize.com
+ * open progress
+ * merge in
+ " if task == []
+
+ if task == ["learn on Memorize.com"]
+ return self.browser args[0]
+ elsif task == ["open progress"]
+ View.open Memorize.filename
+ return ""
+ elsif task == ["merge in"]
+ # View.open Memorize.filename
+ # 1. Delete paragraph
+ bounds = Tree.sibling_bounds
+ new_lines = View.delete bounds[0], bounds[3]
+
+ txt = File.read Memorize.filename
+
+ # Iterate through each new line > and run this code
+ new_lines.split("\n").each do |line|
+
+ # Add number to index
+
+ line1 = txt[/.+/] # => "| [[2,4,1,4],[3]]"
+ biggest = line1.scan(/\d+/).map{|o| o.to_i}.max + 1
+ txt.sub!("],", ",#{biggest}],") # => "| [[2,3,1,3,5],[4]]"
+ line.sub!(/^ \|/, "| #{biggest}")
+ txt << "#{line}\n"
+ end
+ File.open(Memorize.filename, "w") { |f| f << txt }
+
+ # 2. Merge it into the file
+ # | [[2,3,1,3],[4]]
+ # Top line > get biggest number
+ # Add it before > "],"
+ return ""
+ end
+
+ # If task is "~memorize", it'll just continue on as normal expand, and start
+
+
+
+ return self.start
+ end
+
+ # '+ show answer', so show it...
+ if args[0] == "show answer"
+ o = self.new(self.extract_view_txt)
+ o.show_answer
+ o.display
+ return ""
+ end
+
+ # '+ I was wrong', so move to next step...
+ if args[0] == "I was wrong"
+ o = self.new(self.extract_view_txt)
+ if task == [] # ^O, so do "right" instead of "wrong"
+ o.i_was_right
+ else # ^X, so do "wrong"
+ o.i_was_wrong
+ end
+ o.display
+ return ""
+ end
+
+ # 'I was right', so move to next step...
+ if args[0] == "I was right"
+ o = self.new(self.extract_view_txt)
+ o.i_was_right
+ o.display
+ return ""
+ end
+ end
+
+ def self.filename
+ name = View.file || View.name
+
+ dir = File.expand_path "~/.xiki/misc/tmp/"
+
+ FileUtils.mkdir_p dir
+ "#{dir}/round1"+name.gsub(/[^a-z.]/i, '-')
+ end
+
+ def self.start
+
+ # Clear out file state of any previous runs
+ File.open(self.filename, "w") { |f| f << "" }
+
+ # Extract text > contiguous |... lines...
+
+ bounds = Tree.sibling_bounds(:must_match=>"[|+]")
+ txt = View.txt bounds[0], bounds[3]
+
+ return "
+ - Error > every line must be in this format:
+ | question : answer
+ " if txt.split("\n").find{|o| o !~ / : /}
+
+ View.delete bounds[0], bounds[3]
+ o = self.new(txt)
+ o.start
+
+ View << "#{o.indent}- memorizing:\n"
+ o.display
+
+ ""
+
+ end
+
+ def start
+ @choices = ["+ show answer"]
+
+ @pending = @completed.reduce([{}, 1]) do |acc, o|
+ acc[0][acc[1]] = o#[/ (.+)/, 1]
+ acc[1] += 1
+ acc
+ end[0]
+
+ # Shuffle (try up to 6 times if 1st one still at top)...
+
+ length = @pending.length
+
+ @progress = (1..length).to_a
+ 6.times{ @progress = @progress.sort_by{ rand } if @progress[0] == 1 }
+ @progress = [@progress, []]
+
+ @completed = ["#{@pending[@progress[0][0]].sub(/ : .*/, ' : ?')}"]
+
+ end
+
+ def self.extract_view_txt
+ bounds = Tree.sibling_bounds(:must_match=>"[|+]")
+ txt = View.delete bounds[0], bounds[3]
+ txt
+ end
+
+ def serialize
+
+ # Write hidden part to the file
+ txt = serialize_hidden
+ File.open(self.class.filename, "w") { |f| f << txt }
+
+ # Return visible part
+ return serialize_visible
+ end
+
+ def serialize_visible
+ txt = ""
+ txt = @completed.map{|o| "| #{o}"} + @choices
+ txt = txt.map{|o| "#{o}\n"}.join("")
+
+ # Prepend with "memorizing" if just starting
+
+ txt
+ end
+ def serialize_hidden
+ require 'json'
+
+ txt = ""
+
+ # pending = @pending.map{|k, v| "|*#{k} #{v}"}
+ pending = @pending.map{|k, v| "| #{k} #{v}"}
+
+ progress = @progress.any? ? ["| #{JSON[@progress]}"] : []
+
+ txt = progress + pending
+ txt = txt.map{|o| "#{o}\n"}.join("")
+ txt
+ end
+
+ def deserialize txt
+ txt_hidden = File.read self.class.filename
+ txt << txt_hidden
+ txt = txt.split "\n"
+
+ # raise "- Every line must be of the format 'question : answer'"
+
+ @completed, @choices, @progress, @pending = [], [], [], nil, nil
+
+ @completed << txt.shift while txt.any? && txt[0] =~ / : /
+ @choices << txt.shift while txt.any? && txt[0] =~ /^[+-]/
+
+ @progress = txt.shift
+ @progress = @progress ? JSON[@progress[/\[.+/]] : []
+
+ @pending = txt
+
+ @pending = @pending.reduce({}){|acc, o|
+ match = o.match /(\d+) (.+)/
+ acc[match[1].to_i] = match[2]
+ acc
+ }
+
+ end
+
+ def initialize txt=nil
+
+ @indent = txt[/ +/]
+ txt = txt.unindent
+ txt = Tree.unquote txt
+
+ return if ! txt
+ deserialize txt
+ end
+
+ def inspect
+ {
+ :completed=>@completed,
+ :choices=>@choices,
+ :progress=>JSON[@progress],
+ :pending=>@pending,
+ }.ai
+ end
+
+ def propagate_edits
+
+ active = @completed[-1]
+
+ # If ends with ": ?", just replace with what's before it
+
+ update_this = @pending[@progress[0][0]]
+
+ if active =~ /(.+) : \?$/
+ txt = $1
+ update_this.sub! /.+? : /, "#{txt} : "
+ else
+ update_this.replace active #[/\| (.+)/, 1]
+ end
+ end
+
+ def display
+ txt = serialize
+ txt.gsub! /^/, "#{@indent}"
+
+ # No +... means the finished, so leave cursor at the top
+ if txt !~ /^ *\+/
+ View.<< txt, :dont_move=>1
+ return Line.to_words
+ end
+
+ View << txt
+ Move.up
+ Move.up if Line =~ / *\+ I was right/
+ Line.to_words
+ end
+
+
+ def show_answer
+ propagate_edits
+
+ @completed[-1] = "#{@pending[@progress[0][0]]}"
+ @choices = ["+ I was wrong", "+ I was right"]
+
+ end
+
+
+ def i_was_wrong
+ propagate_edits
+
+ @completed.pop
+
+ previous = @progress[0].shift
+ @progress[0] << previous
+ @progress[0].insert 2, previous if @progress[0].length > 1
+
+ remove_runs
+
+ @completed << "#{@pending[@progress[0][0]].sub(/ : .*/, ' : ?')}"
+ @choices = ["+ show answer"]
+
+ serialize
+ end
+
+ def remove_runs
+ @progress[0] = @progress[0].reduce([[], ""]) do |acc, o|
+ acc[0] << o if acc[1] != o
+ acc[1] = o
+ acc
+ end[0]
+ end
+
+ def i_was_right
+ propagate_edits
+
+ @choices = ["+ show answer"]
+
+ @completed.pop
+
+ previous = @progress[0].shift
+
+ # If that was the last one, show at top, and remember order
+ if ! @progress[0].member? previous
+ @completed << "#{@pending[previous]}"
+ # Remember its order, and remove its redundant value from @pending
+ @progress[1] << previous
+ @pending.delete previous
+ end
+
+ # If that was the last one, they're finished!...
+
+ if @progress[0].empty?
+
+ finished
+ @choices = []
+ @progress = []
+ txt = serialize
+ # Clear out file state of any previous runs
+ File.open(self.class.filename, "w") { |f| f << "" }
+ return txt
+ end
+
+ # Show next question...
+
+ @completed << "#{@pending[@progress[0][0]].sub(/ : .*/, ' : ?')}"
+
+ serialize
+ end
+
+ def finished
+
+ # memorizing on line before, so delete it
+ if Line.value(0) =~ /^ *- memorizing:/
+ View.delete(Line.left(0), Line.left)
+ end
+
+ result = []
+ @completed.each_with_index{|item, i|
+ result[@progress[1][i]-1] = item
+ }
+ @completed = result
+ end
+
+ def self.tasks_save_to_memorize
+
+ # Grab paragraph...
+
+ txt = View.paragraph
+
+ # Open in browser...
+
+ self.browser txt
+
+ end
+
+ # They right-clicked on a "a : b" table at the left margin
+
+ def self.tasks_memorize
+
+ # Grab paragraph...
+
+ txt = View.paragraph
+
+ txt = "memorize/\n#{txt.gsub(/^/, ' | ')}"
+ View.open "memorize", :txt=>txt
+ View.line = 2
+ Launcher.launch
+
+ # TODO > Cut off except final a:b lines (in case heading next to it)
+
+ ""
+ end
+
+ def self.browser txt
+ path = txt.gsub(" : ", ":").gsub("\n", "/").gsub(" ", "-")
+
+ # Memorize.com always interprets commas as delimiter, even
+ # when escaped, so just remove them for now
+ path.gsub! ",", ""
+
+ path = URI.encode path, "\","
+
+ Browser.url "http://memorize.com/#{path}"
+ "<* opened in browser"
+ end
+
+end
diff --git a/menu/mkdir.rb b/roots/mkdir.rb
similarity index 79%
rename from menu/mkdir.rb
rename to roots/mkdir.rb
index 6d9a187d..e22a0053 100644
--- a/menu/mkdir.rb
+++ b/roots/mkdir.rb
@@ -6,10 +6,10 @@ def self.menu
return "
> Usage
- | Nest @mkdir under a dir path to create the dir, like this:
+ | Nest =mkdir under a dir path to create the dir, like this:
|
| - /tmp/newdir/
- | - @mkdir
+ | - =mkdir
|
" if ! dir
diff --git a/roots/models.rb b/roots/models.rb
new file mode 100644
index 00000000..69dcbde6
--- /dev/null
+++ b/roots/models.rb
@@ -0,0 +1,87 @@
+load "#{Xiki.dir}roots/rails.rb" if ! defined?(Xiki::Rails)
+
+module Xiki
+ class Models
+ MENU_OBSCURED = %`
+ - */
+ @r/.new
+ @r/.last
+ @r/.all
+ - .more/
+ - one record/
+ @r/.find 1
+ - certain fields/
+ @r/.all.map{|o| o.name}
+ - sort/
+ @r/.order("name")
+ @r/.order("name").map{|o| o.name}
+ - columns/
+ @r/.columns
+ - .delete/
+ - one record/
+ - all records/
+ - model/
+ `
+ MENU = %`
+ - .relationships/
+ `
+
+ def self.delete_records name
+ "- Unfinished!"
+ end
+
+ def self.delete name, kind
+
+ dir = Rails.running_dir
+ name = TextUtil.camel_case name
+
+ if kind == "one record"
+ "@r/#{name}.delete 1"
+ elsif kind == "all records"
+ "@r/#{name}.delete_all"
+ elsif kind == "model files"
+ "
+ @#{dir}/
+ % rails destroy model #{name}
+ "
+ end
+
+ end
+
+ def self.menu_after output, *path
+
+ # /, so show list...
+
+ if path.blank?
+ txt = Rails.run_in_app 'Dir.new("#{Rails.root.to_s}/app/models").entries.grep /\.rb$/'
+
+ return txt if txt =~ /\A\|/
+ txt = YAML::load txt
+ # txt = txt.map{|o| "#{o[/(.+)\.rb$/, 1]}/\n"}.join("")
+ txt = txt.map{|o| "+ #{o[/[\w ]+/]}/\n"}.join("")
+ return "#{txt}#{output}"
+ end
+
+ return self.relationships(yield) if path == ["relationships"]
+
+ # /foo/, so populate in output...
+
+ model = TextUtil.camel_case path[0]
+ output.gsub "", model
+
+ end
+
+ def self.relationships options # name=nil
+
+ options[:no_search] = 1
+
+ dir = Rails.running_dir
+ models_dir = "#{dir}/app/models/"
+ txt = "
+ @#{models_dir}
+ - ##^ *(has_|belongs_to )/
+ "
+ end
+
+ end
+end
diff --git a/lib/xiki/tools/mongo.rb b/roots/mongo.rb
similarity index 51%
rename from lib/xiki/tools/mongo.rb
rename to roots/mongo.rb
index a28da2fa..acb1d2f7 100644
--- a/lib/xiki/tools/mongo.rb
+++ b/roots/mongo.rb
@@ -1,37 +1,68 @@
module Xiki
class Mongo
- def self.menu # bucket=nil
- %`
- - .collections/
+
+ # Move into notes?
+ MENU = %`
- docs/
- > Create
- @foo.save({_id:"a", txt:"b"})
+ - create/
+ =foo.save({_id:"a", txt:"b"})
+ - show/
+ =foo.find()
+ =foo.find({_id:"a"})
+ - update/
+ =foo.update({_id:"a"}, {txt:"bbb"})
+ - delete/
+ =foo.remove({_id:"a"})
+ `
+
+ def self.menu_after output, *items
+
+Ol "items", items
+
+ # /, so prepend the collections...
- > Show
- @foo.find()
- @foo.find({_id:"a"})
+ return "#{self.collections.map{|o| "+ #{o}\n"}.join("")}#{output}" if items.blank?
- > Update
- @foo.update({_id:"a"}, {txt:"bbb"})
+ # MENU handled output, so do nothing
+
+ return if output
+Ol()
+ # No output, so we have to handle
+ # "#{self.collections(*items).map{|o| "+ #{o}\n"}.join("")}"
+ "#{self.collections(*items)}"
- > Delete
- @foo.remove({_id:"a"})
- `.unindent
end
- def self.collections collection=nil
+
+ def self.collections collection=nil, doc=nil
collection.sub!(/\/$/, '') if collection
- # If /, list databases
+ # /, so list databases
- if collection.nil?
+ if ! collection
json = self.run 'printjson(db._adminCommand("listDatabases"))'
o = JSON[json]
return o["databases"].map{|d| "#{d['name']}/"}
end
- self.run("db.#{collection}.find()").gsub(/^/, '| ')
+ # /db/, so list records
+
+ if ! doc
+ txt = self.run("db.#{collection}.find()")
+ txt = %Q`> No documents yet. Create one?\n{ "_id":"id", "name":"Steve", "description":"whatever"}` if txt == ""
+ return txt.gsub(/^/, '| ')
+ end
+
+ # /db/doc, so save
+
+ doc.sub!(/^\| /, '')
+ command = "db.#{collection}.save(#{doc})"
+
+ txt = self.run command # .gsub(/^/, '| ')
+
+ "<* - saved!"
+
end
def self.run command
@@ -46,11 +77,14 @@ def self.run command
end
txt.sub /(.+\n){2}/, '' # Delete first 2 lines
-
end
def self.init
+ # - Is this being called anywhere?
+ # - Maybe have item in menu that saves it to startup
+ # - maybe just shows file syntax for adding to a file!
+
Launcher.add(/^db\./) do |l| # General db... lines
l.strip!
txt = self.run l
diff --git a/roots/mysql/default.conf b/roots/mysql/default.conf
new file mode 100644
index 00000000..66a97bf9
--- /dev/null
+++ b/roots/mysql/default.conf
@@ -0,0 +1,9 @@
+> The db to use when none is specified
+- default db/
+ foo
+- user/
+ root
+#- password/
+# p@ssw0rd
+# - host/
+# localhost
diff --git a/menu/mysql/index.rb b/roots/mysql/mysql_index.rb
similarity index 56%
rename from menu/mysql/index.rb
rename to roots/mysql/mysql_index.rb
index 5e908d8b..444ff8a4 100644
--- a/menu/mysql/index.rb
+++ b/roots/mysql/mysql_index.rb
@@ -7,14 +7,14 @@ class Mysql
"
MENU = "
- - @tables/
- - @dbs/
+ =tables/
+ =dbs/
- .setup/
- .start/
- .start in background/
- stop/
- @ $ ps -eo pcpu,pid,user,args | grep mysql | grep -v grep
- @ $ kill _
+ =$ ps -eo pcpu,pid,user,args | grep mysql | grep -v grep
+ =$ kill _
- db/
- .create/
- .drop/
@@ -24,81 +24,80 @@ class Mysql
- .install/
- config/
> Main config file
- @ /etc/my.cnf
+ =/etc/my.cnf
> Example config files
- @ /usr/local/Cellar/mysql/5.5.25/support-files/
+ =/usr/local/Cellar/mysql/5.5.25/support-files/
- **cnf/
- docs/
- misc commands/
> Drop db
- @ % mysqladmin -u root drop foo
+ =% mysqladmin -u root drop foo
|
> Others
- @ technologies/mysql/
+ =technologies/mysql/
- old/
- - @columns/
+ =columns/
> Or just type some sql here
| show tables
- @conf/
+ =conf/
"
def self.menu_before *args
- return if args[0] !~ /^select /
+ return if args[0] !~ /^(select|delete|update|show|create|describe|insert) /
- # select..., so take control
+ # Not |... or :..., so don't handle...
+
+ # sql statement, so take control...
options = yield
+ args[0].sub! /^: /, ''
+
self.select options, args
end
def self.install
- if Environment.os == :unix
+ if Environment.os == "linix"
"
> Installing Mysql
- @% sudo apt-get install mysql-server
+ =% sudo apt-get install mysql-server
"
- elsif Environment.os == :osx
+ elsif Environment.os == "osx"
"
> Installing Mysql
For now, this just has the mac / homebrew instructions. Fork xiki on github to add docs for other platforms.
> Install using homebrew
- - 1. double-click to install) @ % brew install mysql
+ - 1. double-click to install) =% brew install mysql
- 2. look at the output) and run the commands it tells you to run
> More
See this link for more info on installing:
- @http://www.mysql.com/downloads/mysql/
+ =http://www.mysql.com/downloads/mysql/
"
end
end
- # def self.menu_after txt, *args
- # return nil if txt
- # ENV['no_slash'] = "1"
- # Tree.quote self.run(nil, ENV['txt'])
- # end
-
def self.start
- Console.sync "mysql.server start", :dir=>"/tmp/"
- "@flash/- started!"
+ Shell.sync "mysql.server start", :dir=>"/tmp/"
+ "<* started!"
end
- # def self.start_in_background
- # Console.run "mysql.server start"
- # end
+ def self.conf options
+ Xi.new options[:conf]
+ end
def self.default_db options
- conf = Xi.new options[:conf]
+ conf = self.conf options
if(! conf || ! conf["default db"])
raise "
- > No default db defined. Define it?
- @conf/mysql/
+ > You haven't set up your db config yet
+ | Expand this:
+ =conf/mysql/
"
end
@@ -106,11 +105,20 @@ def self.default_db options
end
def self.tables *args
+ options = yield
db = self.default_db yield
- self.dbs db, *args
+ self.dbs_internal options, db, *args
end
def self.dbs db=nil, table=nil, *row
+
+ options = yield
+ self.dbs_internal options, db, table, *row
+
+ end
+
+ def self.dbs_internal options=nil, db=nil, table=nil, *row
+
key, row = row[0] =~ /^\d+$/ ?
row : [nil, row[0]]
@@ -121,16 +129,16 @@ def self.dbs db=nil, table=nil, *row
# /, so list db's...
if db.nil?
- txt = self.run('', 'show databases')
+ txt = self.run(options, '', 'show databases')
return txt.split[1..-1].map{|o| "#{o}/"}
end
# /db/..., so just list the tables...
if table.nil?
- txt = self.run(db, 'show tables')
+ txt = self.run(options, db, 'show tables')
if txt.blank?
- return "> No tables exist. Create one?\n- @mysql/setup/table/create/#{db}/"
+ return "> No tables exist. Create one?\n=mysql/setup/table/create/#{db}/"
end
return txt.split[1..-1].map{|o| "#{o}/"}
end
@@ -144,16 +152,16 @@ def self.dbs db=nil, table=nil, *row
if row.nil?
sql = "select * from #{table} limit 400"
- out = self.run(db, sql)
+ out = self.run(options, db, sql)
- out = "No records, create one?\n#{self.dummy_row(db, table)}" if out.blank?
+ out = "No records, create one?\n#{self.dummy_row(options, db, table)}" if out.blank?
return Tree.quote out #.gsub(/^/, '| ')
end
# /db/table/row, so save...
- self.save db, table, row
- "@flash/- saved record!"
+ self.save options, db, table, row
+ "<* saved record!"
end
@@ -164,16 +172,19 @@ def self.db_by_key db, table, key, row
if ! row
# /db/table/row, so save...
sql = "select * from #{table} where id = #{key}"
- row = self.run(db, sql)
+ row = self.run(db, sql, options)
row = self.record_hash row
- return row.to_yaml.split("\n")[1..-1].join("\n").gsub(/^/, ': ')
+
+ # =commit/colons
+
+ return row.to_yaml.split("\n")[1..-1].join("\n").gsub(/^/, '| ')
end
# /db/table/key/row, so save...
self.save db, table, row
- "@flash/- todo: save!"
+ "<* save!"
end
# Returns key:value string to show record to the user
@@ -184,8 +195,8 @@ def self.record_hash row
hash
end
- def self.dummy_row db=nil, table=nil
- fields = self.fields db, table
+ def self.dummy_row options, db=nil, table=nil
+ fields = self.fields options, db, table
examples = {
"int"=>"1",
"varchar"=>"foo",
@@ -197,8 +208,8 @@ def self.dummy_row db=nil, table=nil
fields.join("\t")
end
- def self.fields db, table=nil
- txt = self.run db, "desc #{table}"
+ def self.fields options, db, table=nil
+ txt = self.run options, db, "desc #{table}"
txt.sub(/^.+\n/, '').split("\n").map{|o|
l = o.split("\t")
[l[0], l[1].sub(/\(.+/, '')] }
@@ -211,8 +222,8 @@ def self.create what, db=nil, name=nil, columns=nil
end
if what == "db"
- txt = Console.run "mysqladmin -u root create #{db}", :sync=>true
- return ".flash - created db!"
+ txt = Shell.run "mysqladmin -u root create #{db}", :sync=>true
+ return "<* created db!"
end
if name.nil?
@@ -222,11 +233,11 @@ def self.create what, db=nil, name=nil, columns=nil
if columns.nil?
return "
- : id int not null auto_increment primary key,
- : name VARCHAR(20),
- : details text,
- : datestamp DATE,
- : timestamp TIME,
+ | id int not null auto_increment primary key,
+ | name VARCHAR(20),
+ | details text,
+ | datestamp DATE,
+ | timestamp TIME,
"
end
@@ -236,44 +247,39 @@ def self.create what, db=nil, name=nil, columns=nil
);
"
- out = self.run(db, txt)
+ options = yield
+ out = self.run(options, db, txt)
- ".flash - created table!"
+ "<* created table!"
end
- # def self.drop what, name=nil
- # if name.nil?
- # return what == "db" ? self.dbs : self.tables
- # end
-
- # if what == "db"
- # txt = Console.run "mysqladmin -u root drop #{name}" #, :sync=>true
- # return
- # end
- # out = self.run(@default_db, "drop table #{name}")
+ def self.run options, db, sql
- # ".flash - dropped table!"
- # end
+ db ||= self.default_db options
- def self.run db, sql
+ conf = self.conf options
+ user, password, host = conf["user"], conf["password"], conf["host"]
- db ||= self.default_db
+ user = user ? "-u #{user}" : "-u root"
+ password = password ? "-p #{password}" : ""
+ host = host ? "-h #{host}" : ""
File.open("/tmp/tmp.sql", "w") { |f| f << sql }
- out = Console.run "mysql -u root #{db} < /tmp/tmp.sql", :sync=>true
+ command = "mysql #{user} #{db} #{password} #{host} < /tmp/tmp.sql"
+ out = Shell.command command
- raise "> Mysql doesn't appear to be installed. Install it?\n- @mysql/setup/install/" if out == "sh: 1: mysql: not found\n"
- raise "> Mysql doesn't appear to be running. Start it?\n- @mysql/setup/start/" if out =~ /^ERROR.+Can't connect/
- raise "| Database '#{db}' doesn't exist. Create it?\n- @mysql/setup/db/create/#{$1}/" if out =~ /^ERROR.+Unknown database '(.+)'/
- raise "| Table doesn't exist. Create it?\n- @mysql/setup/table/create/#{$1}/" if out =~ /^ERROR.+Table '.+\.(.+)' doesn't exist/
+ raise "> Mysql doesn't appear to be installed. Install it?\n= mysql/setup/install/" if out == "sh: 1: mysql: not found\n"
+ raise "> Mysql doesn't appear to be running. Start it?\n= mysql/setup/start/" if out =~ /^ERROR.+Can't connect/
+ raise "| Database '#{db}' doesn't exist. Create it?\n= mysql/setup/db/create/#{$1}/" if out =~ /^ERROR.+Unknown database '(.+)'/
+ raise "| Table doesn't exist. Create it?\n= mysql/setup/table/create/#{db}/#{$1}/" if out =~ /^ERROR.+Table '.+\.(.+)' doesn't exist/
raise Tree.quote(out) if out =~ /^ERROR/
out
end
- def self.save db, table, row
+ def self.save options, db, table, row
if row =~ /\n/
# Inspect row (k=>v\n...)...
@@ -281,37 +287,37 @@ def self.save db, table, row
hash = YAML::load row
txt = hash.map{|k, v| "#{k}=\"#{v}\"" }.join(", ")
else
- fields = self.fields db, table
+ fields = self.fields options, db, table
# Normal row (tabs and no linebreaks)...
- row = row.sub(/^\| /, '').split("\t")
+ # row = row.sub(/^\| /, '').split("\t")
+ row = row.sub(/^: /, '').split("\t")
txt = fields.map{|o| o[0]}.each_with_index.map{|o, i| "#{o}='#{row[i]}'"}.join(", ")
end
sql = "INSERT INTO #{table} SET #{txt} ON DUPLICATE KEY UPDATE #{txt}"
- self.run db, sql
+ self.run options, db, sql
end
def self.select options, args
sql, row = args
options[:no_slash] = 1
- default_db = self.default_db(options)
+ default_db = self.default_db options
# select..., so run and return results
if ! row
txt = self.run default_db, args[0]
- return txt.gsub /^/, '| '
+ return txt.gsub /^/, ': '
end
table = sql[/from (.+?)( |$)/i, 1]
- Ol "table", table
self.save default_db, table, row
- ".flash - TODO saved!"
+ "<* - saved!"
end
# Launcher.add "columns" do |path|
@@ -323,18 +329,15 @@ def self.select options, args
# end
def self.def_patterns
- Xiki.def(/^select /) do |path, options|
+
+ Xiki.def(/\A(select [^\/]+ from |delete from |show table |create table |describe table |insert into )/) do |path, options|
+
+ # Removed for now > was interjecting when
+ # - command named "update xikihub/"
+
Xiki["mysql/#{options[:path]}"]
end
- end
- # Maybe make the "default_conf" menu item be where it gets the
- # default conf
- def self.default_conf
- "
- > The db to use when none is specifed
- - default db: ?
- "
end
end; end
diff --git a/menu/nav_history.rb b/roots/nav_history.rb
similarity index 100%
rename from menu/nav_history.rb
rename to roots/nav_history.rb
diff --git a/roots/network.menu b/roots/network.menu
new file mode 100644
index 00000000..cfca7ebf
--- /dev/null
+++ b/roots/network.menu
@@ -0,0 +1,5 @@
+= ip/
+= curl/
+= ports/
++ pairing location/
+ ! `scselect pairing`
diff --git a/roots/node.rb b/roots/node.rb
new file mode 100644
index 00000000..095f916a
--- /dev/null
+++ b/roots/node.rb
@@ -0,0 +1,89 @@
+module Command
+ class Node
+ MENU = %`
+ | txt = "Some node code"
+ | console.log(txt)
+ - .controller/
+ - docs/
+ - headers/
+ | console.log(JSON.stringify(req.headers, null, 2));
+ - env vars/
+ | res.write(JSON.stringify(process.env, null, 2));
+ - method/
+ | console.log(req.method);
+ - url/
+ | console.log(req.url);
+ - request params/
+ | var url = require("url");
+ | var _url = url.parse(req.url, true);
+ | console.log(JSON.stringify(_url, null, 2));
+ - read a file sync/
+ | var fs = require('fs');
+ | var txt = fs.readFileSync('./recipe.txt', 'utf8');
+ | console.log(txt);
+ - run in prod mode/
+ % export NODE_ENV=production
+ `
+
+ def self.menu_after output, *args
+ return output if output
+
+ if args.empty?
+ View.prompt("Enter some code to run in node.js")
+ return "| "
+ end
+
+ return self.block if args == ['block']
+ Tree.quote JavascriptHandler.eval(args[0])
+
+ end
+
+ def self.wrap_controller code
+
+ %`
+ var http = require('http');
+ http.createServer(function (req, res) {
+ res.writeHead(200, {"Content-Type": "text/plain"});
+
+ #{code}
+ }).listen(1338, '127.0.0.1');
+ console.log('Server running at http://127.0.0.1:1338/');
+
+ end
+
+ def self.controller *args
+
+ return "
+ | // This is a sample controller. Edit and expand to run it.
+ | console.log('got one request');
+ | res.end('Hello Node World!');
+ " if args.empty?
+
+ code = self.wrap_controller args[0]
+ self.run_controller code
+ end
+
+ def self.run_controller code
+ File.open("/tmp/controller.js", "w") { |f| f << code }
+
+ Buffers.delete "node" if View.buffer_open? "node"
+
+ Shell.run "node controller.js", :dir=>"/tmp/", :buffer=>"node", :dont_move=>1
+ $el.sit_for 0.2
+ Browser.url "http://localhost:1338"
+
+ "<* showing in browser!"
+ end
+
+ def self.block
+ left = Line.right + 1
+ ignore, ignore, right = View.block_positions "^>"
+
+ txt = Xiki::Node.run View.txt(right, left)
+ Block >> txt
+
+ nil
+ end
+
+ end
+end
diff --git a/roots/node_js.menu b/roots/node_js.menu
new file mode 100644
index 00000000..75de0f75
--- /dev/null
+++ b/roots/node_js.menu
@@ -0,0 +1 @@
+<< node/
diff --git a/roots/note_markers.rb b/roots/note_markers.rb
new file mode 100644
index 00000000..7ad14d8a
--- /dev/null
+++ b/roots/note_markers.rb
@@ -0,0 +1,44 @@
+# No path, so show all labels from :t...
+
+prefix = Keys.prefix
+
+if args == []
+
+ # Get latest "notes.notes" and "links.notes" contents...
+
+ todo = Notes.extract_markers :file=>"%n", :limit=>100
+
+ # Remove dups
+ todo = todo.split("\n", -1).uniq.join("\n")
+
+ txt = todo
+
+ options[:hotkey] = 1
+ options[:omit_slashes] = 1
+
+ return txt
+end
+
+# Item passed, so navigate or open...
+
+task = options[:task]
+
+# Root task, so show items...
+
+return "* navigate\n* run" if task == []
+
+# Open or run, so navigate to it...
+
+View.kill if ! task && View.name == "run labels/"
+
+# files/..., so pull it off
+launch_options = {}
+if args.length == 2 and args[0] == "files"
+ args.shift
+ launch_options[:bookmark] = "%links"
+end
+
+launch_options[:label] = args[0]
+launch_options[:go] = 1 if task == ["navigate"]
+
+Launcher.do_last_launch launch_options
diff --git a/roots/notes.rb b/roots/notes.rb
new file mode 100644
index 00000000..9b1f8abd
--- /dev/null
+++ b/roots/notes.rb
@@ -0,0 +1,114 @@
+class Xiki::Menu::Notes
+
+ MENU = %`
+ - docs/
+ - summary/
+ | This menu lists out the notes in ~/xiki/. It lets you drill
+ | into them and navigate to individual notes and update.
+ |
+ | It can be used two different ways, by using @notes by itself,
+ | or nesting @notes under another menu.
+ - nesting/
+ - intro/
+ | You can nest @notes under other menus, like so:
+ = foo/
+ = notes/
+
+ | It will use ~/xiki/foo.xiki. This is a convenience mechanism
+ | for you to create notes that are associated with menus.
+ - example/
+ > 1. if you have a file like this
+ =~/xiki/foo.xiki
+ | > Bar
+ | Some notes about bar.
+ |
+ | > Baz
+ | Some notes about baz.
+
+ > 2. When you expand, it will look like this
+ = foo/
+ = notes/
+ > Bar
+ > Baz
+
+ | Then you can expand the headings and edit inline.
+ - keys/
+ > open+menu
+ | Prompts for a key or two, and opens the matching note from ~/xiki/
+ > as+update
+ | Save the note instead of navigating to it
+ - api/
+ > Turn notes wiki text into html
+ =Notes.to_html "> Heading\\njust text\\n"
+ `
+
+ def self.menu_after output, *path
+
+ options = yield
+
+ # Prepend parent to items if foo/=menu...
+
+ items, task = options[:items], options[:task]
+
+ # foo/=notes/, so use foo as name...
+
+ if path[0] !~ /\A\w/ # Only if there's not a menu-ish item underneath.
+
+ if parent = Xiki.menuish_parent(options)
+
+ # We're nested under a menu-like item, so use it as the name
+ (items||=[]).unshift parent
+ output = nil # Blank out output so we won't get mislead below
+
+ # $ foo/=notes/, so use foo as name...
+
+ elsif parent = options[:ancestors] && options[:ancestors][-1][/\A\$ (\w+)/, 1]
+
+ # We're nested under a menu-like item, so use it as the name
+ (items||=[]).unshift parent
+ output = nil # Blank out output so we won't get mislead below
+ end
+
+ end
+
+ # /, so list notes at top...
+
+ if ! items
+ if task == ["source"]
+ Launcher.open "~/xiki/\n + **\.xiki$/"
+ return ""
+ end
+
+ # In future, should probably look in all XIKI_PATH dirs > borrow from > =all
+
+
+ # List .xiki files in ~/xiki/ sorted by date...
+
+ files = FileTree.files_in_dir(File.expand_path("~/xiki/"), :date_sort=>1)[1]
+
+ files.each{|o| o.sub! /.+\//, ''}
+
+ return files.select{|o| o =~ /^\w.*\.xiki$/}.map{|o| "+ #{o.sub(/\.xiki$/, '').gsub(/_/, ' ')}/\n"}.join('')
+
+ end
+
+ # /foo and output output from MENU, so just show it...
+
+ return output if output
+
+ # /foo, so delegate to ~/commands dir...
+
+ name = items.slice!(0)
+ path = name.gsub(' ', '_')
+
+ # Call .drill directly
+
+ txt = Notes.drill path, *items, options
+
+ Ol "items", items # => []
+
+ return txt if txt
+
+ end
+
+end
diff --git a/roots/ok.rb b/roots/ok.rb
new file mode 100644
index 00000000..5f7dc103
--- /dev/null
+++ b/roots/ok.rb
@@ -0,0 +1,32 @@
+# At left margin, so close this view
+
+if Line.indent == ""
+ View.close
+end
+
+# Indented under something, so jump to parent and collapse
+
+# Go to parent
+Tree.to_parent
+
+# Collapse
+Tree.collapse
+
+
+# "* task", so collapse
+if Line.value =~ /^ *\*/
+
+ indent = Line.indent
+
+ # Delete option item
+ Line.delete
+ # "~ option item" at left margin, so leave cursor there
+ if indent == ""
+ View >> "\n"
+ else
+ Line.previous
+ end
+end
+
+
+""
diff --git a/roots/options.rb b/roots/options.rb
new file mode 100644
index 00000000..d5b2bcdd
--- /dev/null
+++ b/roots/options.rb
@@ -0,0 +1,45 @@
+module Xiki
+ class Options
+
+ def self.menu *args
+
+ # Grab parent subpath to parse and pre-expand...
+
+ options = yield
+
+ ancestors = options[:ancestors]
+ menu_steps = ancestors.any? && ancestors[-1] == "menu steps/"
+ ancestors.pop if menu_steps
+
+ # Normally, this is more complicated than just using this menu
+
+
+ path = ancestors.any? ? ancestors : options[:path]
+
+ options_result = Expander.expanders path
+
+ if options_result[:menufied]
+ Command.climb_sources options_result
+ end
+
+ options_propigate = options.dup
+ options_result = options.merge! options_result # Merge in original options, so editor and prefix vars etc vars are there (might cause problems?)
+
+ # /, so show options_result...
+
+ txt = TextUtil.ap options_result
+ txt = Tree.quote txt, :char=>"|"
+
+ txt
+
+ end
+
+ def self.yaml txt
+ txt = txt.to_yaml
+ txt.sub! /\A--- \n/, ''
+ txt.gsub!(/ $/, '')
+ txt
+ end
+
+ end
+end
diff --git a/roots/os.rb b/roots/os.rb
new file mode 100644
index 00000000..751aa66d
--- /dev/null
+++ b/roots/os.rb
@@ -0,0 +1,23 @@
+#
+# Shows your operating system and version.
+#
+
+# /etc/os-release exists, so use it (probably linux)...
+
+if txt = File.read("/etc/os-release") rescue nil
+ return Tree.pipe txt[/.+\n.+\n/] # Show the 1st 2 lines
+end
+
+# sw_vers command exists, so use it (probably mac)...
+
+if txt = Shell.command("sw_vers", :raise_error=>1) rescue nil
+ return Tree.pipe txt[/.+\n.+\n/] # Show the 1st 2 lines
+end
+
+"
+| Sorry, your OS was unrecognized, because neither
+| the /etc/os-release file nor the 'sw_vers' shell
+| command could be found to give clues about your
+| OS and its version.
+|
+"
diff --git a/roots/os_path.rb b/roots/os_path.rb
new file mode 100644
index 00000000..7b88a554
--- /dev/null
+++ b/roots/os_path.rb
@@ -0,0 +1,4 @@
+ENV['PATH'].
+ split(":").
+ map{|o| "=#{FileTree.add_slash_maybe o}"}.
+ join("\n")
diff --git a/roots/overflow.rb b/roots/overflow.rb
new file mode 100644
index 00000000..fb2a3b83
--- /dev/null
+++ b/roots/overflow.rb
@@ -0,0 +1,24 @@
+txt = args.join("/").strip
+
+# If no arg, prompt to type something
+
+return "=prompt/Type something to search on stack overflow" if txt.blank?
+
+
+# Words not quoted, so grab siblings (if it's not on the same line)...
+
+if txt !~ /\n/ && txt !~ /^:/ && Line !~ /google\//
+ txt = Tree.siblings.join(" ")
+end
+
+txt.sub! /^: /, ''
+txt.gsub! "\n", ' '
+
+
+
+# If arg, look it up
+
+txt = CGI.escape txt
+
+Browser.url "http://stackoverflow.com/search?q=#{txt}"
+nil
diff --git a/roots/parse.rb b/roots/parse.rb
new file mode 100644
index 00000000..233e063e
--- /dev/null
+++ b/roots/parse.rb
@@ -0,0 +1,33 @@
+module Xiki
+ class Parse
+
+ def self.menu *args
+
+ # Grab parent subpath to parse and pre-expand
+
+ options = yield
+ ancestors = options[:ancestors]
+ menu_steps = ancestors[-1] == "menu steps/"
+ ancestors.pop if menu_steps
+
+ path = ancestors.any? ? ancestors : options[:path]
+
+ parse_result = Expander.parse path
+
+ txt = self.ap parse_result
+ txt.gsub! /^ /, ""
+ txt.sub! "[\n [", "[["
+ txt = Tree.quote txt, :char=>"|"
+
+ txt
+ end
+
+ def self.ap txt
+ txt = txt.ai
+ txt.sub! /\A{\n/, ''
+ txt.sub! /\n}\z/, ''
+ txt
+ end
+
+ end
+end
diff --git a/roots/path.menu b/roots/path.menu
new file mode 100644
index 00000000..d750ea2f
--- /dev/null
+++ b/roots/path.menu
@@ -0,0 +1,5 @@
+<< os path/
+<< echo/
+<< menu path/
+<< ruby load path/
+<< emacs/lisp load-path dirs/
diff --git a/lib/xiki/tools/piano.rb b/roots/piano.rb
similarity index 64%
rename from lib/xiki/tools/piano.rb
rename to roots/piano.rb
index 794587f6..4e4c4607 100644
--- a/lib/xiki/tools/piano.rb
+++ b/roots/piano.rb
@@ -1,33 +1,28 @@
-Xiki::Requirer.require_gem 'unimidi', :optional=>1
-
require 'xiki/core/mode'
-
module Xiki
class Piano
- include MIDIator::Notes rescue nil
- include MIDIator::Drums rescue nil
-
- @@unimidi = nil
+ @@fluidsynth = nil
@@held_down = []
@@midi = nil
@@velocity = 100
- @@tempo = 120
- @@probability = 100
- @@melodic = 0
- @@melodic_accumulator = []
- @@climb = 0
- @@pentatonic = false
- @@variation = 0
- @@consistency = 0
- @@mode = 0
- @@octave = 0
- @@program = 1
- @@repeat = 1
- @@names = ['Acoustic Grand Piano', 'Bright Acoustic Piano', 'Electric Grand Piano', 'Honky-tonk Piano', 'Electric Piano 1', 'Electric Piano 2', 'Harpsichord', 'Clavinet', 'Celesta', 'Glockenspiel', 'Music Box', 'Vibraphone', 'Marimba', 'Xylophone', 'Tubular Bells', 'Dulcimer', 'Drawbar Organ', 'Percussive Organ', 'Rock Organ', 'Church Organ', 'Reed Organ', 'Accordion', 'Harmonica', 'Tango Accordion', 'Acoustic Guitar (nylon)', 'Acoustic Guitar (steel)', 'Electric Guitar (jazz)', 'Electric Guitar (clean)', 'Electric Guitar (muted)', 'Overdriven Guitar', 'Distortion Guitar', 'Guitar harmonics', 'Acoustic Bass', 'Electric Bass (finger)', 'Electric Bass (pick)', 'Fretless Bass', 'Slap Bass 1', 'Slap Bass 2', 'Synth Bass 1', 'Synth Bass 2', 'Violin', 'Viola', 'Cello', 'Contrabass', 'Tremolo Strings', 'Pizzicato Strings', 'Orchestral Harp', 'Timpani', 'String Ensemble 1', 'String Ensemble 2', 'Synth Strings 1', 'Synth Strings 2', 'Choir Aahs', 'Voice Oohs', 'Synth Choir', 'Orchestra Hit', 'Trumpet', 'Trombone', 'Tuba', 'Muted Trumpet', 'French Horn', 'Brass Section', 'Synth Brass 1', 'Synth Brass 2', 'Soprano Sax', 'Alto Sax', 'Tenor Sax', 'Baritone Sax', 'Oboe', 'English Horn', 'Bassoon', 'Clarinet', 'Piccolo', 'Flute', 'Recorder', 'Pan Flute', 'Blown Bottle', 'Shakuhachi', 'Whistle', 'Ocarina', 'Lead 1 (square)', 'Lead 2 (sawtooth)', 'Lead 3 (calliope)', 'Lead 4 (chiff)', 'Lead 5 (charang)', 'Lead 6 (voice)', 'Lead 7 (fifths)', 'Lead 8 (bass + lead)', 'Pad 1 (new age)', 'Pad 2 (warm)', 'Pad 3 (polysynth)', 'Pad 4 (choir)', 'Pad 5 (bowed)', 'Pad 6 (metallic)', 'Pad 7 (halo)', 'Pad 8 (sweep)', 'FX 1 (rain)', 'FX 2 (soundtrack)', 'FX 3 (crystal)', 'FX 4 (atmosphere)', 'FX 5 (brightness)', 'FX 6 (goblins)', 'FX 7 (echoes)', 'FX 8 (sci-fi)', 'Sitar', 'Banjo', 'Shamisen', 'Koto', 'Kalimba', 'Bag pipe', 'Fiddle', 'Shanai', 'Tinkle Bell', 'Agogo', 'Steel Drums', 'Woodblock', 'Taiko Drum', 'Melodic Tom', 'Synth Drum', 'Reverse Cymbal', 'Guitar Fret Noise', 'Breath Noise', 'Seashore', 'Bird Tweet', 'Telephone Ring', 'Helicopter', 'Applause', 'Gunshot']
+ @@tempo ||= 120
+ @@probability ||= 100
+ @@melodic ||= 0
+ @@melodic_accumulator ||= []
+ @@climb ||= 0
+ @@pentatonic ||= false
+ @@variation ||= 0
+ @@consistency ||= 0
+ @@mode ||= 0
+ @@octave ||= 0
+ @@program ||= 1
+ @@repeat ||= 1
+ @@names ||= ['Acoustic Grand Piano', 'Bright Acoustic Piano', 'Electric Grand Piano', 'Honky-tonk Piano', 'Electric Piano 1', 'Electric Piano 2', 'Harpsichord', 'Clavinet', 'Celesta', 'Glockenspiel', 'Music Box', 'Vibraphone', 'Marimba', 'Xylophone', 'Tubular Bells', 'Dulcimer', 'Drawbar Organ', 'Percussive Organ', 'Rock Organ', 'Church Organ', 'Reed Organ', 'Accordion', 'Harmonica', 'Tango Accordion', 'Acoustic Guitar (nylon)', 'Acoustic Guitar (steel)', 'Electric Guitar (jazz)', 'Electric Guitar (clean)', 'Electric Guitar (muted)', 'Overdriven Guitar', 'Distortion Guitar', 'Guitar harmonics', 'Acoustic Bass', 'Electric Bass (finger)', 'Electric Bass (pick)', 'Fretless Bass', 'Slap Bass 1', 'Slap Bass 2', 'Synth Bass 1', 'Synth Bass 2', 'Violin', 'Viola', 'Cello', 'Contrabass', 'Tremolo Strings', 'Pizzicato Strings', 'Orchestral Harp', 'Timpani', 'String Ensemble 1', 'String Ensemble 2', 'Synth Strings 1', 'Synth Strings 2', 'Choir Aahs', 'Voice Oohs', 'Synth Choir', 'Orchestra Hit', 'Trumpet', 'Trombone', 'Tuba', 'Muted Trumpet', 'French Horn', 'Brass Section', 'Synth Brass 1', 'Synth Brass 2', 'Soprano Sax', 'Alto Sax', 'Tenor Sax', 'Baritone Sax', 'Oboe', 'English Horn', 'Bassoon', 'Clarinet', 'Piccolo', 'Flute', 'Recorder', 'Pan Flute', 'Blown Bottle', 'Shakuhachi', 'Whistle', 'Ocarina', 'Lead 1 (square)', 'Lead 2 (sawtooth)', 'Lead 3 (calliope)', 'Lead 4 (chiff)', 'Lead 5 (charang)', 'Lead 6 (voice)', 'Lead 7 (fifths)', 'Lead 8 (bass + lead)', 'Pad 1 (new age)', 'Pad 2 (warm)', 'Pad 3 (polysynth)', 'Pad 4 (choir)', 'Pad 5 (bowed)', 'Pad 6 (metallic)', 'Pad 7 (halo)', 'Pad 8 (sweep)', 'FX 1 (rain)', 'FX 2 (soundtrack)', 'FX 3 (crystal)', 'FX 4 (atmosphere)', 'FX 5 (brightness)', 'FX 6 (goblins)', 'FX 7 (echoes)', 'FX 8 (sci-fi)', 'Sitar', 'Banjo', 'Shamisen', 'Koto', 'Kalimba', 'Bag pipe', 'Fiddle', 'Shanai', 'Tinkle Bell', 'Agogo', 'Steel Drums', 'Woodblock', 'Taiko Drum', 'Melodic Tom', 'Synth Drum', 'Reverse Cymbal', 'Guitar Fret Noise', 'Breath Noise', 'Seashore', 'Bird Tweet', 'Telephone Ring', 'Helicopter', 'Applause', 'Gunshot']
+ @@seed ||= Random.new rand
begin
@@map = {
@@ -40,51 +35,10 @@ class Piano
rescue Exception=>e
end
- def self.menu
-
- %`
- > Pass in notes (start GarageBand first)
- | g cdefg c c
+ # > Pass in notes (start GarageBand first)
+ MENU = %`
+ | g cdefg c c
- .setup/
- - .instrument/
- - most common/
- - Acoustic Grand Piano/
- - Electric Piano 1/
- - Glockenspiel/
- - Vibraphone/
- - Xylophone/
- - Drawbar Organ/
- - Church Organ/
- - Accordion/
- - Acoustic Guitar (nylon)/
- - Distortion Guitar/
- - Electric Bass (finger)/
- - Violin/
- - Tremolo Strings/
- - Pizzicato Strings/
- - Orchestral Harp/
- - Timpani/
- - String Ensemble 2/
- - Synth Strings 2/
- - Choir Aahs/
- - Synth Choir/
- - Orchestra Hit/
- - Trumpet/
- - Flute/
- - Pan Flute/
- - Lead 1 (square)/
- - Lead 2 (sawtooth)/
- - Pad 1 (new age)/
- - Pad 2 (warm)/
- - Pad 4 (choir)/
- - Pad 7 (halo)/
- - Pad 8 (sweep)/
- - FX 3 (crystal)/
- - FX 6 (goblins)/
- - Steel Drums/
- - Woodblock/
- - Taiko Drum/
- - all/
- .tempo/
- 60
- 120
@@ -140,73 +94,81 @@ def self.menu
- 100%
- 75%
- 50%
- - .reset/
+ - .sustain/
+ + on
+ + off
+ - .seed/
+ + 1
+ + 2
+ + 3
+ + foo
+ + .reset
- .random notes/
- examples/
- basics/
- chords/
- @piano/
+ = piano
| A A A B A
| C C D D C
| E F F F E
- two parts/
- @piano/
+ = piano
| CGcCGc C GaCGa CGcCGc C GaCGa
| cde edc d c c de e
- three parts/
- @piano/
+ = piano
| ABCDEFGabcdefghijklmnopqrstuv
| B C D E F G a b c d e f g h
| B C D E F G a
- sharps/
- @piano/
+ = piano
| # # # # #
| ce ecbca Gb baGbG G G a
| #
| C E C A C L B E B N B L H
- drums/
- @piano/
+ = piano
| ' ' ' ' ' '* '
| =@@= @@@=@@= @@@
- unofficial xiki theme song/
- @piano/
+ = piano
| xiki is so great
| P Q P Q P Q P Q P Q P Q P Q P Q
| < < < < < < < < < < < < < < < <
| @ = @@= @ = @@= @ = @@= @ = @@=
- generation/
- variation/
- @piano/
+ = piano
| reset()
| variation()
| aaaaaaaa
- probability/
- @piano/
+ = piano
| reset()
| variation()
| probability(50)
| aaaaaaaaaaaa
- melodic/
- @piano/
+ = piano
| reset()
| variation(1)
| melodic()
| aaaaaaaaaaaa
- climb/
- @piano/
+ = piano
| reset()
| variation(1)
| melodic()
| climb()
| aaaaaaaaaaaa
- consistency/
- @piano/
+ = piano
| reset()
| variation()
| consistency(80)
| aaaaaaaaaaaa
- solo/
- @piano/
+ = piano
| reset()
| repeat(8)
| tempo(55)
@@ -217,7 +179,7 @@ def self.menu
| probability(80)
| abcdefgh
- duet/
- @piano/
+ = piano
| reset()
| repeat(8)
| tempo(35)
@@ -228,7 +190,7 @@ def self.menu
| A A A A
| h h h h
- all together/
- @piano/
+ = piano
| reset()
| repeat(8)
| tempo(45)
@@ -241,10 +203,10 @@ def self.menu
| AAAAAAAA
| H H
- everything random/
- @piano/
+ = piano
| reset()
| repeat(16)
- | tempo(rand(60) + 40)
+ | tempo(rand(80) + 70)
| mode(rand 200)
| variation(rand(6) + 1)
| consistency(rand(50))
@@ -255,10 +217,10 @@ def self.menu
| AAAAAAAAAAAAAAAA
| H H H H
- cool instruments/
- @piano/
+ = piano
| reset()
| repeat(32)
- | tempo(rand(60) + 40)
+ | tempo(rand(80) + 70)
| mode(rand 200)
| variation(rand(6) + 1)
| instrument([112, 108, 107, 102, 101, 100, 98, 97, 96, 95, 89, 88, 87, 86, 80, 54, 50, 49, 46, 45, 34, 12, 11, 10, 9, 8, 4][rand 27])
@@ -271,15 +233,15 @@ def self.menu
| H H H H
- .api/
| Play some notes
- @ Piano.song "abc"
+ =Piano.song "abc"
- .docs/
> Single notes
- - @piano/a/
- - @piano/55/
+ = piano/a
+ = piano/55
> Multiple notes
- - @piano/cde edc d c c de e
- - @piano/some words for fun
+ = piano/cde edc d c c de e
+ = piano/some words for fun
> Modes
modes/
@@ -294,7 +256,6 @@ def self.menu
| - Oo o o oo o O: phrygian
| - Oo o oo o o O: locrian
`
- end
def self.names
@@names
@@ -306,6 +267,22 @@ def self.menu_after menu_output, *args
# Don't interfere if menu did something
return menu_output if menu_output
+ options = yield
+
+ # right-click, so delegate to setup...
+
+
+ if task = options[:task]
+ menu = Xik.new(Tree.children(MENU, "setup", :include_subitems=>1))
+
+ return menu.txt if task == [] && options[:mouse] # Show all if mouse
+
+ return Xiki.expand "piano/setup/#{Path.join task}"
+
+ return menu
+ end
+
+
# If just number, intercept
if args.length == 1 && args[0] =~ /^\d+$/
self.note args[0].to_i
@@ -318,54 +295,88 @@ def self.menu_after menu_output, *args
Move.to_end
Search.forward("^[^(\n]+$")
end
- txt = ENV['txt']
+ txt = args[0]
+ return "=beg/quoted/" if txt !~ /\n/
else
txt = args[0]
end
- self.song txt, :move=>1
+ self.song txt, :move=>1, :dont_raise=>1
nil
end
+ # Piano.jingo "foo"
+ # Piano.jingo "bar"
+ def self.jingo seed, options={}
+ self.song %`
+ aaaaaaaa
+ reset/
+ sustain/on
+ repeat/4
+ tempo/80
+ seed/m#{seed}
+ mode/@@seed.rand(100) - 100
+ vary/3
+ probable/75
+ `.unindent, options
+ self.sustain "off"
+ end
+
def self.song txt, options={}
+
+ txt.gsub!(/\/(.*)/, "(\\1)") # Change foo/1 to foo(1)
+ txt.gsub!(/\(([a-z].*[a-z])\)/i, "(\"\\1\")") # Change foo/bar to foo("bar")
+
@@lines = txt.split("\n")#.reverse
self.extract_functions
- # If only config, just run first ones
- # if @@lines.empty?
- # return self.run_functions @@functions_by_index[0], :include_all
- # end
self.run_functions @@functions_by_index[0], :include_all
repeat = (@@functions_by_index[0]||[]).find{|o| o =~ /^rep(eat)?\(/}
@@repeat = (repeat[/\d+/]||"4").to_i if repeat
- @@repeat.times do |i|
-
- # Start at where cursor is
- if $el
- View.column = Line.value[/.+(\/|\| ?)/].length if options[:move]
- end
-
- longest = @@lines.inject(0){|acc, e| e.length > acc ? e.length : acc}
- longest.times do |j|
- # self.run_functions @@functions_by_index[j] # unless j == 0
- self.run_functions @@functions_by_index[j] unless i == 0
+ begin
+ @@repeat.times do |i|
- sharp = false
- @@lines.each_with_index do |line, track|
- char = line[j] ? line[j].chr : nil
- self.note char, :no_sit=>1, :sharp=>sharp, :track=>track
- sharp = char == "#"
- end
+ # Start at where cursor is
if $el
- Move.forward if options[:move] && View.cursor != Line.right
+ # Comment out for demo?
+ View.column = (Line.value[/^ +(\| )?/] || "").length+1 if options[:move]
+ if Line =~ /piano\//
+ Move.to_axis
+ Search.forward("piano/")
+ end
+ end
+
+ longest = @@lines.inject(0){|acc, e| e.length > acc ? e.length : acc}
+
+ longest.times do |j|
+ self.run_functions @@functions_by_index[j] unless i == 0
+
+ sharp = false
+ @@lines.each_with_index do |line, track| #> |
+ char = line[j] ? line[j].chr : nil
+ self.note char, :no_sit=>1, :sharp=>sharp, :track=>track #> in song:386) ||
+ sharp = char == "#"
+ end
+ if $el
+ # Comment out for demo?
+ Move.forward if options[:move] && View.cursor != Line.right
+ end
+ self.pause #> in song:394) |
end
- self.pause
end
+
+ rescue RuntimeError=>e
+
+ raise e if ! options[:dont_raise] # Unless alternate flag passed in, always raise
+
+ raise e if e.message != "stopped" # :dont_raise, so don't raise if user stopped
+
+ # Swallowed exception, because the user stopped it by typing something
end
self.clear
@@ -426,12 +437,12 @@ def self.letter_to_number letter, options={}
end
def self.apply_variation number, options={}
- return number if rand(100) > (100 - @@consistency) # Do nothing if consistency says to stop
+ return number if @@seed.rand(100) > (100 - @@consistency) # Do nothing if consistency says to stop
- random = rand(@@variation+1)
+ random = @@seed.rand(@@variation+1)
if @@climb == 0
- random *= ((-1) ** rand(2)) # Half of the time, make it decrease note
+ random *= ((-1) ** @@seed.rand(2)) # Half of the time, make it decrease note
end
if @@melodic == 1
@@ -449,7 +460,7 @@ def self.apply_variation number, options={}
end
def self.apply_probability number
- return 0 if rand(100) > @@probability
+ return 0 if @@seed.rand(100) > @@probability
number
end
@@ -489,9 +500,18 @@ def self.note letter='a', options={}
number += 1 if options[:sharp]
number += (@@octave * 12)
- channel = 143 + channel
+ channel = 1
+
@@held_down << number
- self.unimidi.puts(channel, number, velocity) # note on message
+
+ # Temp
+ # channel = 158
+
+ # 144 : note on message
+ # 128 : note off message
+
+
+ self.noteon(channel, number, velocity) # note on message #> |||
return if options[:no_sit] # Don't sit if other tracks have same beat
@@ -499,33 +519,53 @@ def self.note letter='a', options={}
nil
end
- def self.unimidi
- @@unimidi ||= UniMIDI::Output.open(0).open
+ def self.fluidsynth_process
+
+ # Best
+ Open3.popen3('fluidsynth ~/projects/soundfonts/acoustic_grand_piano_ydp_20080910.sf2')
+
+ # Decent
+ # Open3.popen3 'fluidsynth ~/projects/soundfonts/25-piano-sf/Giga-Piano.sf2'
+ # Open3.popen3 'fluidsynth ~/projects/soundfonts/25-piano-sf/Fazioli-Grand-Piano-.SF2'
+ # Open3.popen3 'fluidsynth ~/projects/soundfonts/25-piano-sf/Grand-Piano.sf2'
+ # Open3.popen3 'fluidsynth ~/projects/soundfonts/25-piano-sf/Motif-ES6-Concert-Piano.SF2'
+ # Open3.popen3 'fluidsynth ~/projects/soundfonts/25-piano-sf/Fantasy-Piano.sf2'
+
end
- def self.send channel, number, value
+ def self.noteoff channel, number, velocity
+ if ! @@fluidsynth
+ sleep 0.25
+ @@fluidsynth = self.fluidsynth_process
+ end
+ @@fluidsynth[0].puts("noteoff #{channel} #{number} #{velocity}\n")
+ end
- output = UniMIDI::Output.open(0)
- output.open do |output|
- output.puts(channel, number, 90) # note on message
+ def self.noteon channel, number, velocity
+ if ! @@fluidsynth
+ @@fluidsynth = self.fluidsynth_process
+ sleep 0.25
end
+ @@fluidsynth[0].puts("noteon #{channel} #{number} #{velocity}\n")
end
def self.pause
- # Ol << "!"
pause = @@tempo * 4
pause = pause / 60.0
pause = 1 / pause
$el ? $el.sit_for(pause) : sleep(pause)
- Piano.clear
+
+
+ input = $el.read_char_exclusive "", nil, pause
+ self.clear #> ||
+ raise "stopped" if input
end
def self.clear chan=1
- chan = (chan + 143) - 16
while number = @@held_down.shift do
- self.unimidi.puts(chan, number, 90) # note on message
+ self.noteoff(chan, number, 90) # note on message #> |||
end
end
@@ -536,6 +576,7 @@ def self.keydef letter, note, channel=1, velocity=126
end
def self.midi
+ raise "not used?"
@@midi || self.connect
end
@@ -661,9 +702,8 @@ def self.reset
@@octave = 0
@@program = 1
@@repeat = 1
- # @@seed = nil
- ".flash - success!"
+ "<*"
end
#
@@ -674,6 +714,7 @@ def self.control_change a, b, c
end
def self.connect
+ raise "not used?"
@@midi = MIDIator::Interface.new
@@midi.use :dls_synth
# This doesn't work in Lion :(
@@ -682,40 +723,57 @@ def self.connect
@@midi
end
- def self.velocity txt="126"; @@velocity = txt.to_i; ".flash - updated!"; end
- def self.tempo txt="120"; @@tempo = txt.to_i; ".flash - updated!"; end
- def self.probability txt="50"; @@probability = txt.to_s.sub('%', '').to_i; ".flash - updated!"; end
+ def self.velocity txt="126"; @@velocity = txt.to_i; "<*"; end
+ def self.tempo txt="120"
+ @@tempo = txt.to_i
+ "<*"
+ end
+ def self.probable txt="50"; self.probability txt; end
+ def self.probability txt="50"; @@probability = txt.to_s.sub('%', '').to_i; "<*"; end
+ def self.sustain txt="on"
+ if txt == "on" # Sustain petal on
+ self.noteon(0xB0, 0x40, 1)
+ return ""
+ end
+ self.noteon(0xB0, 0x40, 0) # Sustain petal off
+ "<*"
+ end
+ def self.vary txt="2"
+ self.variation txt
+ end
def self.variation txt="2"
@@variation = txt.to_i
- # if @@seed # If seed set manually, just use it
- # seed = @@seed
- # else # Else auto-generate seed
- # seed = rand 999_999_999_999_999_999_999
- # end
# srand seed
- ".flash - updated!"
+ "<*"
+ end
+ def self.melodic txt="1"; @@melodic = txt.to_i; "<*"; end
+ def self.climb txt="1"; @@climb = txt.to_i; "<*"; end
+ def self.pentatonic txt="1"; Ol.<<(txt); @@pentatonic = [true, "on", 1].member?(txt); "<*"; end
+ def self.consistency txt="50"; @@consistency = txt.to_s.sub('%', '').to_i; "<*"; end
+ def self.octave txt="0"; @@octave = txt.to_i; "<*"; end
+ def self.repeat txt="4"; @@repeat = txt.to_i; "<*"; end
+ # def self.seed txt; @@seed = txt.to_i; "<*"; end
+
+ def self.seed i
+ i = i.to_i if i =~ /\A\d+\z/
+ i = i.hash if ! i.is_a? Fixnum
+ @@seed = Random.new i
+ "<*"
end
- def self.melodic txt="1"; @@melodic = txt.to_i; ".flash - updated!"; end
- def self.climb txt="1"; @@climb = txt.to_i; ".flash - updated!"; end
- def self.pentatonic txt="1"; Ol.<<(txt); @@pentatonic = [true, "on", 1].member?(txt); ".flash - updated!"; end
- def self.consistency txt="50"; @@consistency = txt.to_s.sub('%', '').to_i; ".flash - updated!"; end
- def self.octave txt="0"; @@octave = txt.to_i; ".flash - updated!"; end
- def self.repeat txt="4"; @@repeat = txt.to_i; ".flash - updated!"; end
- # def self.seed txt; @@seed = txt.to_i; ".flash - updated!"; end
def self.mode txt=nil
return @@mode if txt.nil?
if txt.to_s == "random"
- random = (-2..9).to_a[rand 7]
+ random = (-2..9).to_a[@@seed.rand 7]
@@mode = random
- return ".flash - updated to #{random}!"
+ return "<* updated to #{random}!"
end
@@mode = txt.to_i
- ".flash - updated!"
+ "<*"
end
class << self
@@ -735,6 +793,7 @@ class << self
end
def self.driver
+ raise "not used?"
@@midi ||= self.connect
end
@@ -756,11 +815,12 @@ def self.random_notes range=nil, notes=nil
txt = ""
8.times do
- txt << range[rand range.length]
+ txt << range[@seed.rand range.length]
end
return "| #{txt}"
end
+ # Todo > Do this the right way
self.song ENV['txt']
nil
end
@@ -768,5 +828,5 @@ def self.random_notes range=nil, notes=nil
end
Piano.init # Define mode
- Menu.drums :menu=>'piano'
+
end
diff --git a/roots/ports.menu b/roots/ports.menu
new file mode 100644
index 00000000..dbc1d074
--- /dev/null
+++ b/roots/ports.menu
@@ -0,0 +1,10 @@
+- all ports/
+ ! Tree.quote `lsof -i | grep LISTEN`
+- single port/
+ ! return "=prompt/Type a port" if args.empty?
+ ! Tree.quote `lsof -i :#{args[0]}`
+- all connections/
+ ! Tree.quote `netstat -p tcp`
+- examples/
+ - processes listening on a port/
+ =$ lsof -i :8161 | grep LISTEN
\ No newline at end of file
diff --git a/roots/preferences.menu b/roots/preferences.menu
new file mode 100644
index 00000000..8874c5f7
--- /dev/null
+++ b/roots/preferences.menu
@@ -0,0 +1 @@
+<< conf/
diff --git a/roots/processes.menu b/roots/processes.menu
new file mode 100644
index 00000000..f8dae7a6
--- /dev/null
+++ b/roots/processes.menu
@@ -0,0 +1,6 @@
+- all/
+ ! Tree.quote `ps -eo pcpu,pid,user,args | sort -k 1 -r`
+- Mac Activity Monitor/
+ @app/Utilities/Activity Monitor
+<< cpu/
+<< kill/
diff --git a/roots/questions.rb b/roots/questions.rb
new file mode 100644
index 00000000..a67faf94
--- /dev/null
+++ b/roots/questions.rb
@@ -0,0 +1 @@
+Xiki[:memorize, args, options]
diff --git a/roots/quick_topics.rb b/roots/quick_topics.rb
new file mode 100644
index 00000000..14fbf9ad
--- /dev/null
+++ b/roots/quick_topics.rb
@@ -0,0 +1,23 @@
+# /, so return favorite topics...
+
+if args == []
+
+ return "* edit" if task == []
+ return View.open("~/.xiki/misc/favorite/topics.xiki") if task == ["edit"]
+
+ file = File.expand_path "~/.xiki/misc/favorite/topics.xiki"
+ txt = File.read(file) rescue nil
+
+ if txt
+ options[:hotkey] = 1
+ end
+
+ txt ||= "<* - No favorites yet!"
+ return txt
+end
+
+
+# /topic, so replace parent and expand...
+
+"<< #{args[0]}"
+
diff --git a/roots/quit.menu b/roots/quit.menu
new file mode 100644
index 00000000..9205dc7a
--- /dev/null
+++ b/roots/quit.menu
@@ -0,0 +1 @@
+<< unsaved/just quit
diff --git a/roots/r.rb b/roots/r.rb
new file mode 100644
index 00000000..d2d9384c
--- /dev/null
+++ b/roots/r.rb
@@ -0,0 +1,47 @@
+# Require 'rails' menu if not yet loaded
+load "#{Xiki.dir}roots/rails.rb" if ! defined?(Xiki::Rails)
+
+module Xiki
+
+ # Runs code in a rails app. Implants xikidev_controller.rb into the
+ # app? > actually currently suggests installing xiki plugin
+ # ...so it can pass messages to it to eval.
+ #
+ # For security it only works in dev mode, only accepts local requests,
+ # and reads input from a file on disk.
+ class R
+
+ def self.menu *args
+ code, yaml = args
+
+ return "=beg/quoted/" if args[-1] && args[-1] !~ /\n/
+
+ # /, so show last r/... commands...
+
+ if ! code
+ Ol["This should get just r/, not all r... menu roots!"]
+ txt = Launcher.last "r", :exclude_path=>1
+ txt.gsub! /^- (\| )?/, '| '
+ txt = txt.split("\n").uniq.join("\n")
+ return txt
+ end
+
+ # ENV['no_slash'] = "1"
+
+
+ # /code/, so run it in app...
+
+ if ! yaml
+ txt = Rails.run_in_app code
+ txt = Tree.quote(txt) if txt !~ /^\s+(>|\|)/
+ return txt
+ end
+
+ # /code/yaml, so save the yaml (assuming it's an active record object)...
+Ol "yaml", yaml
+
+ Rails.run_in_app yaml, :yaml=>1
+ end
+
+ end
+end
diff --git a/roots/rails.rb b/roots/rails.rb
new file mode 100644
index 00000000..c85c33d6
--- /dev/null
+++ b/roots/rails.rb
@@ -0,0 +1,381 @@
+require 'xiki/core/ruby_console'
+
+module Xiki
+ class Rails
+
+ CODE_SAMPLES = %q<
+ # Show options to help create new rails app
+ - Show options: Rails.menu
+ >
+
+ MENU_HIDDEN = "
+ .running dir/
+ .nest/
+ "
+
+ MENU = "
+ - .start/
+ - .generate/
+ - app/
+ - plugin/
+ - model/
+ - resource/
+ - controller/
+ - scaffold/
+ - .inspect/
+ - .routes/
+ - .interact/
+ - .rails console/
+ - .sqlite console/
+ - =models/
+ - .setup/
+ - .db/
+ - .migrate/
+ - .use rspec/
+ - .rails version/
+ "
+
+ def self.nest
+ Tree.to_parent
+ Tree.collapse
+ Line.delete
+ txt = "
+ /tmp/rails1/
+ =rails/
+ ".unindent
+
+ View.<< txt, :dont_move=>1
+ Line.next
+ Line.to_beginning
+ ""
+ end
+
+
+ def self.menu_before *path
+
+ dir = Tree.closest_dir yield[:ancestors]
+
+
+ return self.eval_in_app(path[0]) if path.length == 1 && path[0] =~ /\n/
+
+ # Don't intercede if already rails app or trying to generate
+ return nil if ["generate", "nest"].member?(path[0]) || File.exists?("#{dir}/app")
+
+ # Not nested, so offer to nest...
+
+ # Not a rails dir, so offer to nest under a dir...
+
+ return "
+ > Rails app doesn't exist. Generate it?
+ - generate/app/
+ "
+ end
+
+
+ def self.eval_in_app code
+
+ Ol "continue here!!"
+ # Call localhost:3000/xikidev > to eval the code in the app
+
+ txt = Rails.run_in_app code
+
+ end
+
+
+ def self.rails_version
+ "| #{`rails --version`}"
+ end
+
+ def self.use_rspec
+ dir = Tree.closest_dir yield[:ancestors]
+
+ txt = "
+ =#{dir}
+ - 1. Add these lines:
+ - Gemfile
+ |+group :development, :test do
+ |+ gem 'rspec-rails'
+ |+end
+ |
+ - 2. Run these commands:
+ % bundle
+ % rails g rspec:install
+ |
+ - 3. Delete the test/ dir:
+ % rm -r test/
+ "
+ end
+
+ def self.sqlite_console
+ Shell.run "sqlite3 db/development.sqlite3", :dir=>self.dir(yield), :buffer=>"sqlite console", :dont_move=>1
+ "<* opened console in other view!"
+ end
+
+ def self.rails_console
+ Shell.run "rails c", :dir=>self.dir(yield), :buffer=>"rails console", :dont_move=>1
+ "<* opened console in other view!"
+ end
+
+ def self.generate what, name=nil, detail=nil
+
+ examples = "
+ > 1. Example fields
+ | name:string
+ | details:text
+ | summary:text
+ | quantity:integer
+ | price:decimal
+ | delivery:boolean
+ | purchased_at:datetime
+ | user:references
+ | --no-timestamps
+ ".unindent
+
+ dir = self.dir(yield)
+
+ options = yield
+
+ case what
+ when "app"
+ dir, stem = File.dirname(dir), File.basename(dir)
+
+ Shell.run "rails new \"#{stem}\" --skip-bundle", :dir=>dir, :dont_move=>1
+ return "- generating rails app in other view..."
+ when "plugin"
+ Shell.run "rails plugin new . --skip-bundle --full", :dir=>dir, :dont_move=>1
+ return "- generating rails app in other view..."
+ when "model", "resource", "scaffold"
+ return View.prompt "Enter a name" if ! name
+ return examples if ! detail
+ fields = Tree.txt.gsub("\n", ' ').strip
+ Shell.run "rails g #{what} #{name} #{fields}", :dir=>dir, :dont_move=>1
+ dir = options[:ancestors][-1]
+ return "
+ | Generating #{what} in other view. Now run the migrations:
+ =#{dir}% rake db:migrate
+ | Then restart the server
+ =current/rails server
+ | The url
+ =http://localhost:3000/#{name}
+ "
+ when "controller"
+ return View.prompt "Enter a name" if ! name
+ return View.prompt "Enter an action" if ! detail
+ Shell.run "rails g controller #{name} #{detail}", :dir=>dir, :dont_move=>1
+ return "
+ - Generating controller in other view...
+ =http://localhost:3000/#{name}/#{detail}
+ =#{dir}app/
+ - controllers/#{name}_controller.rb
+ - views/#{name}/#{detail}.html.erb
+ "
+ end
+
+ "- Don't know how to generate a '#{what}'!"
+ end
+
+ def self.dir options={}
+
+ # Nevermind > If no args, hit :3000 to get dir
+
+ Tree.closest_dir options[:ancestors]
+ end
+
+ def self.start *args
+
+ # If 1st arg is number, assume it's the port
+ port = args[0] =~ /^\d+$/ ? args.shift : nil
+
+ # If 'browse', just bring up in browser
+ if args == ['browse']
+ Firefox.url "http://localhost:#{port || 3000}/"
+ return "<* opened in browser!"
+ end
+
+ command = "rails s"
+ command << " -p #{port}" if port
+
+ Shell.run command, :dir=>self.dir(yield), :buffer=>"rails server", :dont_move=>1
+
+ # Check whether it's already running
+ "| Rails app was already running\n- browse/"
+ "| Starting rails app in other view...\n- browse/"
+
+ end
+
+ def self.command txt, options
+ Shell.run txt, :dir=>self.dir(options)
+ end
+
+ def self.migrate
+ self.command "rake db:migrate", yield
+ end
+
+ def self.routes
+ self.command "rake routes", yield
+ end
+
+ def self.eval *args
+
+ if args.blank?
+ return "
+ > Put some code here, to run it in the context of a controller
+ | request.methods
+ "
+ end
+
+ # Text passed, so run put in controller method and call
+
+ # Start server if necessary
+ # And install the dev controller?
+
+ txt = Tree.txt
+
+ "- TODO) implement calling dev_controller"
+ end
+
+ def self.run_in_app txt, options={}
+
+ # If just code passed, run it...
+
+ if options[:yaml]
+ # If yaml passed, deduce code to save model, and run...
+
+ txt = %`
+ txt = #{txt.inspect}
+ mods = YAML::load(txt)
+
+ mods = [mods] if ! mods.is_a?(Array)
+ mods.each do |mod|
+ mod.instance_variable_set('@partial_writes', false)
+
+ existing = mod.class.where :id=>mod.id
+ mod.instance_variable_set('@new_record', true) if existing.empty?
+
+ mod.save
+ end
+ `
+ end
+
+ # Tmp > Hard-coded
+ file = File.expand_path "/Users/craig/Dropbox/xikihub_rails/tmp/rails_run_tmp.txt"
+
+ FileUtils.mkdir_p File.dirname(file) # Create dir if it doesn't exist
+ File.open(file, "w") { |f| f << txt }
+
+ # Temp > Hard-coded > !!!!!!!
+ uri = URI("http://docker.loc/_xikidev")
+
+
+
+ response = Net::HTTP.get_response(uri)
+
+ return "| The rails server doesn't appear to be running. Start default server?\n=rails/start/" if response == :exception
+
+ if response.response.is_a?(Net::HTTPNotFound)
+
+ return self.suggest_installing_dev_controller if response.body =~ / ", :return_path=>1).should == ["| "]
# end
# end
@@ -46,7 +46,7 @@
# | dotsies.org : /xiki@xiki.org/var/www/dotsies.org/
# ".unindent
-# Menu.menu_to_hash(input).should == {
+# Command.menu_to_hash(input).should == {
# "dotsies.loc"=>"/projects/dotsies.org/www/",
# "dotsies.org"=>"/xiki@xiki.org/var/www/dotsies.org/",
# }
@@ -62,9 +62,9 @@
it "finds source for simple menu" do
options = {:name=>"path", :path=>"path/"}
- Menu.root_sources_from_path_env options
+ Command.root_sources_from_path_env options
options.should == {
- :menufied => "/projects/xiki/menu/path",
+ :menufied => "/projects/xiki/roots/path",
:name => "path",
:path => "path/",
:sources => [["path.menu"], :incomplete]
@@ -72,13 +72,13 @@
end
it "finds source for menu with space" do
- options = {:name=>"menu_path", :path=>"menu path/"}
- Menu.root_sources_from_path_env options
+ options = {:name=>"command_path", :path=>"command path/"}
+ Command.root_sources_from_path_env options
options.should == {
- :menufied => "/projects/xiki/menu/menu_path",
- :name => "menu_path",
- :path => "menu path/",
- :sources => [["menu_path.rb"], :incomplete]
+ :menufied => "/projects/xiki/roots/commands_path",
+ :name => "command_path",
+ :path => "command path/",
+ :sources => [["command_path.rb"], :incomplete]
}
end
end
@@ -90,14 +90,21 @@
it "recognizes a menu" do
options = {:name=>"path", :path=>"path/"}
- Menu.expands?(options)
+ Command.expands?(options)
options.should == {
:name => "path",
:path => "path/",
- :sources => [["path.menu"], :incomplete ],
- :menufied => "/projects/xiki/menu/path",
+ :sources => [["path.rb"], :incomplete ],
+ :menufied => "/projects/xiki/roots/path",
:expanders => [Menu]
}
end
+
+ it "doesn't try to handle when extension" do
+ options = {:name=>"path", :path=>"path.txt/", :extension=>".txt"}
+ Command.expands?(options)
+ options[:expanders].should == [MenuSource]
+ end
+
end
diff --git a/spec/conf_handler_spec.rb b/spec/conf_handler_spec.rb
index 18c2164c..dacb002b 100644
--- a/spec/conf_handler_spec.rb
+++ b/spec/conf_handler_spec.rb
@@ -6,45 +6,5 @@
%w"xiki/core/tree".each {|o| require o}
-describe ConfHandler, "#parse" do
- it "makes a hash of one line" do
- txt = "- password: BaaRamEwe"
- ConfHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
- end
+# No longer used - Xik.new now is used instead
- it "makes a hash of two lines" do
- txt = "
- - password: BaaRamEwe
- - motto: Baa
- ".unindent
- ConfHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
- end
-
- it "ignores headings and blank lines" do
- txt = "
- > Ignore headings
- - password: BaaRamEwe
-
- - motto: Baa
- ".unindent
- ConfHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
- end
-
- it "doesn't require dashes" do
- txt = "password: BaaRamEwe"
- ConfHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
- end
-
- # it "handles nesting" do
- #
- # - What about the "- motto: Baa" vs "- motto/Baa" question?
- # - For the xi syntax
- #
- # txt = "
- # - sheep password: Baa Ram Ewe
- # - sheep mottos
- # - Just keep baa'ing
- # - Curlyness is next to godliness
- # ".unindent
-
-end
diff --git a/spec/conf_loading_handler_spec.rb b/spec/conf_loading_handler_spec.rb
index ea87dbd7..dc93e4be 100644
--- a/spec/conf_loading_handler_spec.rb
+++ b/spec/conf_loading_handler_spec.rb
@@ -6,46 +6,55 @@
%w"xiki/core/tree".each {|o| require o}
-describe ConfLoadingHandler, "#parse" do
- it "makes a hash of one line" do
- txt = "- password: BaaRamEwe"
- ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
- end
-
- it "makes a hash of two lines" do
- txt = "
- - password: BaaRamEwe
- - motto: Baa
- ".unindent
- ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
- end
-
- it "ignores headings and blank lines" do
- txt = "
- > Ignore headings
- - password: BaaRamEwe
-
- - motto: Baa
- ".unindent
- ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
- end
-
- it "doesn't require dashes" do
- txt = "password: BaaRamEwe"
- ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
- end
-
-
- # it "handles nesting" do
- #
- # - What about the "- motto: Baa" vs "- motto/Baa" question?
- # - For the xi syntax
- #
- # txt = "
- # - sheep password: Baa Ram Ewe
- # - sheep mottos
- # - Just keep baa'ing
- # - Curlyness is next to godliness
- # ".unindent
-
-end
+
+
+# Menus should use Xik.new, to parse.
+# - or whatever else they want, like YAML::load
+
+
+
+
+
+# describe ConfLoadingHandler, "#parse" do
+# it "makes a hash of one line" do
+# txt = "- password: BaaRamEwe"
+# ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
+# end
+
+# it "makes a hash of two lines" do
+# txt = "
+# - password: BaaRamEwe
+# - motto: Baa
+# ".unindent
+# ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
+# end
+
+# it "ignores headings and blank lines" do
+# txt = "
+# > Ignore headings
+# - password: BaaRamEwe
+
+# - motto: Baa
+# ".unindent
+# ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe", "motto"=>"Baa"}
+# end
+
+# it "doesn't require dashes" do
+# txt = "password: BaaRamEwe"
+# ConfLoadingHandler.parse(txt).should == {"password"=>"BaaRamEwe"}
+# end
+
+
+# # it "handles nesting" do
+# #
+# # - What about the "- motto: Baa" vs "- motto/Baa" question?
+# # - For the xi syntax
+# #
+# # txt = "
+# # - sheep password: Baa Ram Ewe
+# # - sheep mottos
+# # - Just keep baa'ing
+# # - Curlyness is next to godliness
+# # ".unindent
+
+# end
diff --git a/spec/expander_spec.rb b/spec/expander_spec.rb
index 96311dc0..e2edbeb0 100644
--- a/spec/expander_spec.rb
+++ b/spec/expander_spec.rb
@@ -1,20 +1,21 @@
$:.unshift "spec/"
require './spec/spec_helper'
-
-Dir["./lib/xiki/*_handler.rb"].each{|o|
+require "xiki/core/launcher"
+require "xiki/core/xik"
+Dir["./lib/xiki/handlers/*_handler.rb"].each{|o|
require o.sub("./lib/", "")
}
-
-%w"path code tree menu menu_suggester pre_pattern pattern file_tree bookmarks".each {|o| require "xiki/core/#{o}"}
+%w"path code tree menu command_suggester pre_pattern pattern file_tree bookmarks".each {|o| require "xiki/core/#{o}"}
require 'xiki/core/expander'
require 'xiki/core/pattern'
+require 'xiki/core/control_tab'
# describe Expander, "#expand" do
describe Expander, "#extract_ancestors" do
it "pulls out one path" do
- args = "a/@b/", {}
+ args = "a/=b/", {}
Expander.extract_ancestors *args
args.should == ["b/", {:ancestors=>["a/"]}]
end
@@ -26,7 +27,7 @@
end
it "ignores quoted path" do
- args = "a/| a/@b/", {}
+ args = "a/| a/=b/", {}
Expander.extract_ancestors *args
args.should == ["b/", {:ancestors=>["a/| a/"]}]
end
@@ -43,10 +44,10 @@
end
it "expands bookmarks" do
- stub(Bookmarks).[]("$d") {"/tmp/dir/"}
- Expander.expand_file_path("$d/a//b").should == "/tmp/dir/a//b"
- Expander.expand_file_path("$d").should == "/tmp/dir"
- Expander.expand_file_path("$d/").should == "/tmp/dir/"
+ stub(Bookmarks).[]("%d") {"/tmp/dir/"}
+ Expander.expand_file_path("%d/a//b").should == "/tmp/dir/a//b"
+ Expander.expand_file_path("%d").should == "/tmp/dir"
+ Expander.expand_file_path("%d/").should == "/tmp/dir/"
end
it "doesn't remove double slashes for home and current dir" do
@@ -55,8 +56,8 @@
end
it "doesn't remove double slashes for bookmarks" do
- stub(Bookmarks).[]("$f") {"/tmp/file.txt"}
- Expander.expand_file_path("$f//").should == "/tmp/file.txt//"
+ stub(Bookmarks).[]("%links") {"/tmp/file.txt"}
+ Expander.expand_file_path("%links//").should == "/tmp/file.txt//"
end
end
@@ -84,17 +85,17 @@
it "handles menufied paths" do
Expander.parse("/tmp/a//").should ==
- {:menufied=>"/tmp/a"}
+ {:menufied=>"/tmp/a", :path=>"/tmp/a//"}
end
it "handles menufied path with items" do
Expander.parse("/tmp/a//b/").should ==
- {:menufied=>"/tmp/a", :items=>["b"]}
+ {:menufied=>"/tmp/a", :items=>["b"], :path=>"/tmp/a//b/"}
end
it "handles filesystem root menufied path" do
Expander.parse("//").should ==
- {:menufied=>"/"}
+ {:menufied=>"/", :path=>"//"}
end
it "handles name that looks kind of menufied" do
@@ -155,12 +156,12 @@
end
it "handles ancestors in string" do
- Expander.parse("z/@a/").should ==
+ Expander.parse("z/=a/").should ==
{:name=>"a", :ancestors=>["z/"], :path => "a/"}
end
it "handles ancestors with path in string" do
- Expander.parse("x/y/@a/b/").should ==
+ Expander.parse("x/y/=a/b/").should ==
{:name=>"a", :items=>["b"], :ancestors=>["x/y/"], :path => "a/b/"}
end
@@ -183,6 +184,83 @@
:path => "echo/a;/b"
}
end
+
+
+ it "pulls out extension" do
+ Expander.parse("echo.txt").should == {
+ :name=>"echo",
+ :extension=>".txt",
+ :path=>"echo.txt"
+ }
+ end
+
+ it "pulls out path and extension" do
+ Expander.parse("echo.txt/a/b").should == {
+ :items=>["a", "b"],
+ :name=>"echo",
+ :extension=>".txt",
+ :path=>"echo.txt/a/b"
+ }
+ end
+
+ it "pulls out period-only extension" do
+ Expander.parse("echo.").should == {
+ :name=>"echo",
+ :extension=>".",
+ :path=>"echo."
+ }
+ end
+
+
+
+
+
+ it "moves task items into :task" do
+ options = Expander.parse("hi/* delete")
+ options.should == {
+ :task=>"delete",
+ :name=>"hi",
+ :path=>"hi",
+ }
+ end
+
+ # Also test these...
+ # options = Expander.parse("select * from/* delete")
+ # options = Expander.parse("/tmp/* delete")
+
+ it "moves pattern task items into :task" do
+ options = Expander.parse("select * from hi/* delete")
+ options.should == {
+ :task=>"delete",
+ :path=>"select * from hi"
+ }
+ end
+
+ it "moves file task items into :task" do
+ options = Expander.parse("/tmp/* delete")
+ options.should == {
+ :task=>"delete",
+ :file_path=>"/tmp"
+ }
+ end
+
+
+end
+
+describe Expander, "#expand_literal_command method" do
+ before(:each) do
+ stub_menu_path_dirs # Has to be before each for some reason
+ end
+
+ it "expands command text with path" do
+ txt = Expander.expand_literal_command "a/\n b", :path=>"a"
+ txt.should == 'b'
+ end
+
+ it "expands when embedded code" do
+ txt = Expander.expand_literal_command "a/\n ! 1 + 1", :path=>"a"
+ txt.should == '2'
+ end
end
describe Expander, "#expand method" do
@@ -201,13 +279,12 @@
end
it "takes a path list as 2nd arg" do
- Ol["Maybe pull 'echo' out as its own menu - and pass options to make it cached?!"]
Expander.def(:echo) { |path| path.inspect }
Expander.expand("echo", ["a", "b"]).should == '["a", "b"]'
end
- it "expands menu in MENU_PATH" do
+ it "expands menu in XIKI_PATH" do
Expander.expand("dd").should == "+ a/\n+ b/\n+ cccc/\n+ craig/\n+ keith/\n"
end
@@ -218,4 +295,24 @@
it "expands menufied path" do
Expander.expand("#{Xiki.dir}spec/fixtures/menu/dr//").should == "+ a/\n+ b/\n"
end
+
+ # it "expands when literal text of command passed" do
+ # Expander.expand(["a"], :command_text=>"a/\n b").should == "b"
+ # end
+
+ # it "expands when literal text of command with path" do
+ # Expander.expand(["a/b"], :command_text=>"a/\n b/\n c").should == "c"
+ # end
+
+end
+
+describe Expander, ".extract_task_items" do
+ before(:each) do
+ stub_menu_path_dirs # Has to be before each for some reason
+ end
+
+ it "extracts when normal" do
+ # Expander.def(:echo) { |path| path.inspect }
+ # Expander.expand("echo").should == '[]'
+ end
end
diff --git a/spec/file_tree_spec.rb b/spec/file_tree_spec.rb
index bb07b765..60f2c1f6 100644
--- a/spec/file_tree_spec.rb
+++ b/spec/file_tree_spec.rb
@@ -26,8 +26,8 @@
end
it "handles bookmarks" do
- (!!FileTree.matches_root_pattern?("$aa")).should == true
- (!!FileTree.matches_root_pattern?("$..")).should == false
+ (!!FileTree.matches_root_pattern?(":aa")).should == true
+ (!!FileTree.matches_root_pattern?(":..")).should == false
end
it "declines non-file paths" do
diff --git a/spec/fixtures/menu/m.menu b/spec/fixtures/menu/m.menu
deleted file mode 100644
index 183b6618..00000000
--- a/spec/fixtures/menu/m.menu
+++ /dev/null
@@ -1,9 +0,0 @@
-- a/
- - ay/
- > Ay
- - Nothing, just that
- - eh/
- - aye/
-- b/
- - bee/
- - be/
diff --git a/spec/fixtures/menu/no.notes b/spec/fixtures/menu/no.notes
deleted file mode 100644
index e7caaa5b..00000000
--- a/spec/fixtures/menu/no.notes
+++ /dev/null
@@ -1,6 +0,0 @@
-> Hey from n
-- Should notes files just be shown verbatim
-
-> Hoo from n
-- Should notes files just be shown verbatim
- - or, should they be expanded per headings
diff --git a/spec/fixtures/menu/r.rb b/spec/fixtures/menu/r.rb
deleted file mode 100644
index 150f606e..00000000
--- a/spec/fixtures/menu/r.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class R
- def self.menu
- "
- - r1/
- - r11/
- "
- end
-end
diff --git a/tests/git_test.rb b/spec/git_spec.rb
similarity index 69%
rename from tests/git_test.rb
rename to spec/git_spec.rb
index ddbcdbfb..da4b88e6 100644
--- a/tests/git_test.rb
+++ b/spec/git_spec.rb
@@ -1,5 +1,45 @@
-require 'test/unit'
-$:.unshift "../"
+$:.unshift "spec/"
+require './spec/spec_helper'
+
+require 'xiki/core/git'
+
+describe Git, "#status_to_hash_new" do
+ it "Populates untracked key" do
+ txt = "
+ ?? a.txt
+ ?? b.txt
+ ".unindent
+ Git.status_to_hash(txt).should == {:untracked=>[["untracked", "a.txt"], ["untracked", "b.txt"]], :unadded=>[], :added=>[]}
+ end
+
+ it "Populates unadded key" do
+ txt = "
+ AM a.txt
+ M committed.txt
+ D deleteme.txt
+ ".unindent
+ Git.status_to_hash(txt).should == {:unadded=>[["modified", "a.txt"], ["modified", "committed.txt"], ["deleted", "deleteme.txt"]], :untracked=>[], :added=>[["new file", "a.txt"]]}
+ end
+
+ it "Populates added keys" do
+ txt = "
+ AM a.txt
+ A d/d.txt
+ R rename.txt -> renamed.txt
+ ".unindent
+ Git.status_to_hash(txt)[:added].should == [["new file", "a.txt"], ["new file", "d/d.txt"], ["renamed", "rename.txt -> renamed.txt"]]
+ end
+
+end
+
+
+
+__END__
+
+# Old test before moving to rspec:
+
+#require 'test/unit'
+#$:.unshift "../"
require 'ol'
require 'core_ext'
require 'yaml'
diff --git a/spec/html_spec.rb b/spec/html_spec.rb
new file mode 100644
index 00000000..e33609f6
--- /dev/null
+++ b/spec/html_spec.rb
@@ -0,0 +1,59 @@
+$:.unshift "spec/"
+module Xiki; end
+require 'xiki/core/tree'
+require 'xiki/core/html'
+require './spec/spec_helper'
+
+describe Html, "#to_html_tags" do
+ it "handles one tag" do
+ Html.to_html_tags("
+ p/
+ hi
+ ".unindent).should == "
+
+ hi
+
+ ".unindent
+ end
+
+ it "handles single with no contents" do
+ Html.to_html_tags("
+ p/
+ ".unindent).should == "
+
+
+ ".unindent
+ end
+
+ it "doesn't confuse comments" do
+ Html.to_html_tags("
+ p/
+ /* hey */
+ ".unindent).should == "
+
+ /* hey */
+
+ ".unindent
+ end
+
+ it "adds closing tags to html" do
+ Html.to_html_tags("
+
+ hi
+ ".unindent).should == "
+
+ hi
+
+ ".unindent
+ end
+
+ it "doesn't close comment tags" do
+ Html.to_html_tags("
+
+ hi
+ ".unindent).should == "
+
+ hi
+ ".unindent
+ end
+end
diff --git a/spec/invoker_spec.rb b/spec/invoker_spec.rb
index e0f8f7f0..1ae11ede 100644
--- a/spec/invoker_spec.rb
+++ b/spec/invoker_spec.rb
@@ -21,18 +21,24 @@
it "two actions" do
Invoker.actionify(["act", "act2", "b"], [true, true, nil]).should == ["act2", ["b"]]
end
+
+ it "doesn't mess up the args" do
+ args = ["Hey you"]
+ Invoker.actionify(args, [true, true, nil]).should == ["hey_you", []]
+ args.should == ["Hey you"]
+ end
end
-describe Invoker, "#extract_ruby_package" do
+describe Invoker, "#extract_ruby_module" do
it "no module" do
- Invoker.extract_ruby_package("
+ Invoker.extract_ruby_module("
class Foo
end
".unindent).should == nil
end
it "extracts one module" do
- Invoker.extract_ruby_package("
+ Invoker.extract_ruby_module("
module Modern
class Foo
end
@@ -41,7 +47,7 @@ class Foo
end
it "extracts two modules" do
- Invoker.extract_ruby_package("
+ Invoker.extract_ruby_module("
module Modern
module Modest
class Foo
@@ -52,7 +58,7 @@ class Foo
end
it "isn't confused by stuff after start of class" do
- Invoker.extract_ruby_package("
+ Invoker.extract_ruby_module("
module Modern
# stuff
class Foo
@@ -63,4 +69,12 @@ module Herring
".unindent).should == "Modern"
end
+ it "handles when all on one line" do
+ Invoker.extract_ruby_module("
+ class Modern::Foo
+ # stuff
+ end
+ ".unindent).should == "Modern"
+ end
+
end
diff --git a/spec/keys_spec.rb b/spec/keys_spec.rb
index 6c4b6d1e..fda78c43 100644
--- a/spec/keys_spec.rb
+++ b/spec/keys_spec.rb
@@ -1,8 +1,22 @@
# $:.unshift "spec/"
-# require 'keys'
-# require 'ol'
+require './spec/spec_helper'
+require 'xiki/core/keys'
+
+describe Keys, "#filter" do
+ it "matches when one letter" do
+ Keys.fuzzy_filter(["javascript", "ruby"], "r").should == "ruby"
+ end
+
+ it "matches when two letters" do
+ Keys.fuzzy_filter(["javascript", "ruby"], "rb").should == "ruby"
+ end
+
+ it "matching first few letters takes prescedence" do
+ Keys.fuzzy_filter(["rebuild", "ruby"], "ru").should == "ruby"
+ end
+
+end
-# describe Keys, "#input" do
# before(:each) do
# View = mock 'View'
diff --git a/spec/memorize_spec.rb b/spec/memorize_spec.rb
new file mode 100644
index 00000000..5b48360b
--- /dev/null
+++ b/spec/memorize_spec.rb
@@ -0,0 +1,47 @@
+# $:.unshift "spec/"
+# require './spec/spec_helper'
+# require 'json'
+
+# %w"path tree".each {|o| require "xiki/core/#{o}"}
+# # %w"path code tree menu menu_suggester pre_pattern pattern file_tree bookmarks".each {|o| require "xiki/core/#{o}"}
+
+# require_relative '../menu/memorize.rb'
+
+# # describe Memorize, "#expand" do
+# describe Memorize, "#propagate_edits" do
+# it "updates when just question" do
+
+# txt = Tree.children(Memorize::MENU_HIDDEN.unindent, "test/deserialize/").gsub /^: /, ''
+# o = Memorize.new txt
+
+# o.propagate_edits
+# o.pending[3].should == "Japana : Tokyo"
+
+# end
+
+# it "updates when just question and answer" do
+# txt = Tree.children(Memorize::MENU_HIDDEN.unindent, "test/deserialize/").gsub /^: /, ''
+
+# txt.sub! "___", "Tokistan"
+
+# o = Memorize.new txt
+
+# o.propagate_edits
+# o.pending[3].should == "Japana : Tokistan"
+# end
+# end
+
+# describe Memorize, "#deserialize" do
+# it "works" do
+
+# txt = Tree.children(Memorize::MENU_HIDDEN.unindent, "test/deserialize/").gsub /^: /, ''
+# Ol.a txt
+
+# o = Memorize.new
+
+# o.deserialize txt
+# o.heading.should == ["> Example"]
+# o.completed.length.should == 3
+# o.progress.should == [[3, 1, 2], []]
+# end
+# end
diff --git a/spec/notes_spec.rb b/spec/notes_spec.rb
index aad7e4e1..eea3ec9e 100644
--- a/spec/notes_spec.rb
+++ b/spec/notes_spec.rb
@@ -2,10 +2,41 @@
require './spec/spec_helper'
-%w"path code tree menu menu_suggester pre_pattern pattern file_tree bookmarks".each {|o| require "xiki/core/#{o}"}
+Dir["./lib/xiki/handlers/*_handler.rb"].each{|o| require o.sub("./lib/", "") }
+%w"xik expander command code_tree path code tree topic notes xikihub_client menu command_suggester pre_pattern pattern file_tree bookmarks topic_expander".each {|o| require "xiki/core/#{o}"}
+
+%w"path code tree menu command_suggester pre_pattern pattern file_tree bookmarks".each {|o| require "xiki/core/#{o}"}
require 'xiki/core/notes'
+describe Notes, "#replace_section" do
+ it "add wiki section" do
+ txt = Expander.expand "xiki api/add wiki section"
+ txt.should == %`
+ | > Foo
+ | New
+ | section prepended!
+ |
+ |
+ | > Herring
+ | Unrelated heading
+ `.unindent
+ end
+
+ it "update existing wiki section replace" do
+ txt = Expander.expand "xiki api/update existing wiki section replace"
+ txt.should == %`
+ | > Foo
+ | New
+ | section replaced old!
+ |
+ |
+ `.unindent
+ end
+
+end
+
+
describe Notes, "#drill_split" do
it "pulls out head ang quote" do
Notes.drill_split(["> head", "| quote"]).should == [[], ["> head"], ["| quote"]]
diff --git a/spec/ol_spec.rb b/spec/ol_spec.rb
index 3a0fc60b..3ac7106c 100644
--- a/spec/ol_spec.rb
+++ b/spec/ol_spec.rb
@@ -68,14 +68,6 @@ def define_vars
before(:each) do
@line = "/projects/foo/controllers/accounts.rb:24:in `check'"
end
-
- it "writes line to log with label like - class.method (line):" do
- Ol.should_receive(:pause_since_last?).and_return false
- Ol.should_receive(:write_to_file).with('/tmp/ds_ol.notes', "- Accounts.check:24) hi\n")
- Ol.should_receive(:write_to_file_lines).with("/tmp/ds_ol.notes", "/projects/foo/controllers/accounts.rb:24\n")
-
- Ol.line "hi", @line, "", "ds"#, @@last_log
- end
end
describe Ol, "#log" do
diff --git a/spec/path_spec.rb b/spec/path_spec.rb
index c49ea00b..5272bc18 100644
--- a/spec/path_spec.rb
+++ b/spec/path_spec.rb
@@ -58,13 +58,27 @@
end
it "splits ancestors when :outer" do
- Path.split("a/b/@c/d/", :outer=>1).should == ["a/b/", "c/d/"]
+ Path.split("a/b/=c/d/", :outer=>1).should == ["a/b/", "c/d/"]
end
it "doesn't get confused by escaped slashes before ats" do
- Path.split("a/b;/@c/d/", :outer=>1).should == ["a/b;/@c/d/"]
+ Path.split("a/b;/=c/d/", :outer=>1).should == ["a/b;/=c/d/"]
end
it "doesn't get confused by escaped slashes ats" do
- Path.split("a/b;/@c/d/", :outer=>1).should == ["a/b;/@c/d/"]
+ Path.split("a/b;/=c/d/", :outer=>1).should == ["a/b;/=c/d/"]
+ end
+
+ it "splits on dollar signs when command" do
+ Path.split("a/$ f", :outer=>1).should == ["a/", "$ f"]
+ end
+ it "splits on dollar signs when blank prompt in middle" do
+ Path.split("a/$/b", :outer=>1).should == ["a/", "$/b"]
+ end
+ it "splits on dollar signs when blank prompt at end" do
+ Path.split("a/$", :outer=>1).should == ["a/", "$"]
+ end
+
+ it "doesn't split when dollar whitout space after it" do
+ Path.split("a/$f", :outer=>1).should == ["a/$f"]
end
@@ -88,11 +102,34 @@
# #Path.split("aa/|b/b/|c/c").should == ["aa", "|b/b", "|c/c"]
# end
+ it "turns ;l into linebreaks" do
+ Path.split("a;lb").should == ["a\nb"]
+ end
+ # Todo > remove this > ;o is the new way
it "turns ;0 into linebreaks" do
Path.split("a;0b").should == ["a\nb"]
end
+
+ it "splits ancestors when spaces" do
+ Path.split("a/b/= c/d/", :outer=>1).should == ["a/b/", "c/d/"]
+ end
+
+
+
+
+ it "splits on colon space" do
+ Path.split("aa/bb: cc").should == ["aa", "bb", "cc"]
+ end
+
+ it "only escapes unescaped colons" do
+ Path.split("aa/bb;: cc").should == ["aa", "bb: cc"]
+ end
+
+
+
+
# it "handles :return_path with pipes" do
# Path.split("dom/|
", :return_path=>1).should == ["|
"]
# end
diff --git a/spec/rest_tree_spec.rb b/spec/rest_tree_spec.rb
new file mode 100644
index 00000000..df07faec
--- /dev/null
+++ b/spec/rest_tree_spec.rb
@@ -0,0 +1,36 @@
+$:.unshift "spec/"
+require './spec/spec_helper'
+
+require 'xiki/core/tree'
+require 'xiki/core/rest_tree'
+
+describe RestTree, "#comment_embedded_menu" do
+
+ it "returns unaltered when no comment" do
+ RestTree.comment_embedded_menu("foo", "foo").should == nil
+ end
+
+ it "uses a menu" do
+ txt = "
+ Hey
+
+ "
+ RestTree.comment_embedded_menu(txt, "").should == "+ foo/\n+ bar/\n"
+ end
+
+ it "uses menu when path" do
+ txt = "
+ Hey
+
+ "
+ RestTree.comment_embedded_menu(txt, "xiki://localhost/foo").should == "- XX\n"
+ end
+
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 135d180b..8e26ffa7 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,11 +1,14 @@
require 'rr'
require 'awesome_print'
+XIKI_SERVER_MODE = false if ! defined?(XIKI_SERVER_MODE)
+
%w"xiki/core/core_ext xiki/core/ol".each {|o| require o}
# RSpec::Runner.configure do |config|
RSpec.configure do |config|
config.mock_with :rr
+ config.expect_with(:rspec) { |c| c.syntax = :should }
end
module Xiki
@@ -17,8 +20,8 @@ def self.dir
def stub_menu_path_dirs
xiki_dir = Xiki.dir
- list = ["#{xiki_dir}spec/fixtures/menu", "#{xiki_dir}menu"]
- stub(Xiki).menu_path_dirs {list}
+ list = ["#{xiki_dir}spec/fixtures/menu", "#{xiki_dir}commands"]
+ stub(Xiki).xiki_path_dirs {list}
end
diff --git a/spec/text_util_spec.rb b/spec/text_util_spec.rb
index 1f21331f..83532de3 100644
--- a/spec/text_util_spec.rb
+++ b/spec/text_util_spec.rb
@@ -1,9 +1,9 @@
$:.unshift "spec/"
+require './spec/spec_helper'
require 'xiki/core/text_util'
describe TextUtil, "#unindent" do
-
it "indents to the left" do
TextUtil.unindent(
" hey
@@ -71,15 +71,29 @@
".gsub(/^ /, '')
end
- it "does nothing when subsequent line not indented" do
- shouldnt_change = "[
+ it "doesn't change indent when any subsequent line not indented" do
+ shouldnt_change = " [
1,
- 2,
- 3
- ]
+ 2
+ not indented
".gsub(/^ /, '')
TextUtil.unindent(shouldnt_change).should == shouldnt_change
end
+
+ it "adds linebreak for consistency when any subsequent line not indented" do
+ TextUtil.unindent(" a\nnot indented").should == " a\nnot indented\n"
+ end
+
+ it "Doesn't remove trailing spaces on last line" do
+ TextUtil.unindent(
+ "
+ hey
+ you
+ ".gsub(/^ /, '')).
+ should == "hey \nyou \n"
+ end
+
+
end
describe TextUtil, "#snake_case" do
@@ -120,3 +134,13 @@
TextUtil.title_case("core-platform").should == "Core Platform"
end
end
+
+describe TextUtil, "#word_wrap" do
+ it "wraps to 5 chars" do
+ TextUtil.word_wrap("hi hi hi", 5).should == "hi hi\nhi"
+ end
+
+ it "wraps to 4 chars" do
+ TextUtil.word_wrap("hi hi hi", 4).should == "hi\nhi\nhi"
+ end
+end
diff --git a/spec/tree_spec.rb b/spec/tree_spec.rb
index bff3fb01..9b2c5bc4 100644
--- a/spec/tree_spec.rb
+++ b/spec/tree_spec.rb
@@ -1,7 +1,7 @@
$:.unshift "spec/"
require './spec/spec_helper'
-%w"tree path".each {|o| require "xiki/core/#{o}"}
+%w"tree path view xik".each {|o| require "xiki/core/#{o}"}
describe Tree, "#traverse" do
@@ -16,7 +16,11 @@
Tree.traverse tree do |array|
paths << array
end
- paths.should == [[["- a/"], "a/"], [["- a/", "- b/"], "a/b/"], [["- c/"], "c/"]]
+ paths.should == [
+ [["- a/"], "a/", 1],
+ [["- a/", "- b/"], "a/b/", 2],
+ [["- c/"], "c/", 3],
+ ]
end
it "handles two-level dropoff and no dropoff" do
@@ -34,11 +38,11 @@
end
paths.should ==[
- [["- a/"], "a/"],
- [["- a/", "- aa/"], "a/aa/"],
- [["- a/", "- aa2/"], "a/aa2/"],
- [["- a/", "- aa2/", "- aaa/"], "a/aa2/aaa/"],
- [["- c/"], "c/"]
+ [["- a/"], "a/", 1],
+ [["- a/", "- aa/"], "a/aa/", 2],
+ [["- a/", "- aa2/"], "a/aa2/", 3],
+ [["- a/", "- aa2/", "- aaa/"], "a/aa2/aaa/", 4],
+ [["- c/"], "c/", 5],
]
end
@@ -55,9 +59,9 @@
paths << array
end
paths.should == [
- [["- hey) a/"], "hey) a/"],
- [["- hey) a/", "- you) b/"], "hey) a/you) b/"],
- [["- c/"], "c/"]
+ [["- hey) a/"], "hey) a/", 1],
+ [["- hey) a/", "- you) b/"], "hey) a/you) b/", 2],
+ [["- c/"], "c/", 3],
]
end
@@ -72,7 +76,8 @@
paths << array
end
paths.should == [
- [["a/"], "a/"], [["a/", "b/"], "a/b/"]
+ [["a/"], "a/", 1],
+ [["a/", "b/"], "a/b/", 2],
]
end
@@ -89,9 +94,9 @@
end
paths.should == [
- [["a/"], "a/"],
- [[nil], ""],
- [["b/"], "b/"],
+ [["a/"], "a/", 1],
+ [[nil], "", 2],
+ [["b/"], "b/", 3],
]
end
@@ -108,10 +113,10 @@
paths << array
end
paths.should == [
- [["a/"], "a/"],
- [["a/", "aa/"], "a/aa/"],
- [["a/", nil], "a/"],
- [["a/", "ab/"], "a/ab/"],
+ [["a/"], "a/", 1],
+ [["a/", "aa/"], "a/aa/", 2],
+ [["a/", nil], "a/", 3],
+ [["a/", "ab/"], "a/ab/", 4],
]
end
@@ -134,16 +139,16 @@
paths << array
end
paths.should == [
- [["a/"], "a/"],
- [["a/", "aa/"], "a/aa/"],
- [["a/", "aa/", "aaa/"], "a/aa/aaa/"],
- [["a/", "aa/", nil], "a/aa/"],
- [["a/", "aa/", "aab/"], "a/aa/aab/"],
- [["a/", nil], "a/"],
- [["a/", "ab/"], "a/ab/"],
- [["a/", "ab/", "aba/"], "a/ab/aba/"],
- [[nil], ""],
- [["b/"], "b/"],
+ [["a/"], "a/", 1],
+ [["a/", "aa/"], "a/aa/", 2],
+ [["a/", "aa/", "aaa/"], "a/aa/aaa/", 3],
+ [["a/", "aa/", nil], "a/aa/", 4],
+ [["a/", "aa/", "aab/"], "a/aa/aab/", 5],
+ [["a/", nil], "a/", 6],
+ [["a/", "ab/"], "a/ab/", 7],
+ [["a/", "ab/", "aba/"], "a/ab/aba/", 8],
+ [[nil], "", 9],
+ [["b/"], "b/", 10],
]
end
@@ -171,6 +176,27 @@
}.should raise_error(RuntimeError)
end
+
+ it "adds slashes when none" do
+ paths = []
+ tree = "
+ a
+ b
+ c
+ ".unindent
+
+ Tree.traverse tree do |array|
+ paths << array
+ end
+ paths.should == [
+ [["a"], "a", 1],
+ [["a", "b"], "a/b", 2],
+ [["c"], "c", 3],
+ ]
+ end
+
+
+
end
@@ -337,8 +363,42 @@
Tree.dotify(tree, path).should == [true]
end
- # TODO: also work when they erroneously put "_" in the menu?
+ it "works when in reverse and a underscore and space" do
+ tree = "
+ - .delete it/
+ ".unindent
+
+ path = ["delete_it"]
+ Tree.dotify(tree, path).should == [true]
+ end
+
+ it "works when a case is different" do
+ tree = "
+ - .Delete it/
+ ".unindent
+
+ path = ["delete_it"]
+ Tree.dotify(tree, path).should == [true]
+ end
+
+ it "works when a hyphen and space" do
+ tree = "
+ - hey-you there/
+ ".unindent
+ path = ["hey-you-there"]
+ Tree.dotify(tree, path).should == [true]
+ end
+
+ it "matches when star" do
+ tree = "
+ - */
+ - .bb/
+ ".unindent
+
+ path = ["aa/bb/"]
+ Tree.dotify(tree, path).should == [nil, true]
+ end
end
@@ -353,7 +413,7 @@
it "grabs siblings when pipe" do
mock(Line).value {"| bb"}
stub(Tree).siblings {["| aa", "| bb"]}
- Tree.leaf("| bb").should == "aa\nbb\n"
+ Tree.leaf("| bb").should == "aa\nbb\n-----"
end
it "grabs siblings when pipe" do
@@ -380,15 +440,15 @@
describe Tree, "#quote" do
it "quotes normal lines" do
- Tree.quote("hey\nyou\n").should == "| hey\n| you\n".unindent
+ Tree.quote("hey\nyou\n").should == ": hey\n: you\n".unindent
end
it "quotes blank lines" do
- Tree.quote("hey\n\nyou\n").should == "| hey\n|\n| you\n".unindent
+ Tree.quote("hey\n\nyou\n").should == ": hey\n:\n: you\n".unindent
end
it "quotes when indented" do
- Tree.quote("hey\n you\n").should == "| hey\n| you\n".unindent
+ Tree.quote("hey\n you\n").should == ": hey\n: you\n".unindent
end
# it "leaves menus unquoted" do
@@ -415,7 +475,7 @@
describe Tree, "#clear_empty_dirs!" do
- it "removes one empty dir" do
+ it "removes one empty dir when no bullets" do
result = Tree.clear_empty_dirs!("
/projects/
empty/
@@ -443,8 +503,8 @@
- /projects/trunk/app/views/assets/details/
+ hey/
- _database.rhtml
- |Database Type
- |Database Name/
+ : Database Type
+ : Database Name/
".unindent).join("\n")
result.should =~ /Database Type/
@@ -456,13 +516,24 @@
- /projects/trunk/
- hey.html
- you.html
- | Database Type
+ : Database Type
".unindent, :quotes=>true).join("\n")
result.should =~ /trunk/
result.should =~ /you/
result.should_not =~ /hey/
end
+
+ it "leaves paths starting with equals" do
+ result = Tree.clear_empty_dirs!("
+ nav history//xiki/search.rb/
+ =/projects/xiki/lib/xiki/core/
+ =/projects/xiki/lib/xiki/core/
+ - search.rb
+ : def self.enter txt=nil
+ ".unindent, :quotes=>true).join("\n")
+
+ end
end
@@ -623,15 +694,15 @@
# assert_equal true, Tree.is_root?(" /hey")
# assert_equal true, Tree.is_root?(" ./hey")
# assert_equal false, Tree.is_root?(" .you")
-# assert_equal true, Tree.is_root?(" $tr/")
-# assert_equal true, Tree.is_root?(" $tr/##abc/")
-# # assert_equal false, Tree.is_root?(" $tr")
+# assert_equal true, Tree.is_root?(" :tr/")
+# assert_equal true, Tree.is_root?(" :tr/##abc/")
+# # assert_equal false, Tree.is_root?(" :tr")
# end
describe Tree, "#children" do
- it "shows 2nd-level items when blank path" do
+ it "shows top items when blank path" do
Tree.children("
- a/
- .b/
@@ -653,6 +724,18 @@
".unindent
end
+ it "converts dashes to pluses for items ending in slash" do
+ Tree.children("
+ - a/
+ - aa/
+ - ab
+ - b/
+ ", "a").should == "
+ + aa/
+ - ab
+ ".unindent
+ end
+
it "returns nil when no match" do
Tree.children("
- a/
@@ -735,32 +818,37 @@
", "a/z").should == "+ aaa/\n"
end
- it "includes all sub-items of items under at sign" do
+ it "matches when root is star" do
Tree.children("
- - @a/
+ - */
+ - bb/
+ ", "a/").should == "+ bb/\n"
+ end
+
+ it "includes all sub-items of items under equals sign" do
+ Tree.children("
+ - =a/
- .b/
- c/
", "").should == "
- + @a/
+ + =a/
+ b/
+ c/
".unindent
+ end
+
+ it "includes all sub-items of items under equals" do
Tree.children("
- - a/
- - @b/
+ - =a/
+ - .b/
- c/
- - d/
- ", "a").should == "
- + @b/
- + c/
- + d/
+ ", "").should == "
+ + =a/
+ + b/
+ + c/
".unindent
end
- # it "doesn't include items under item with at sign" do
- # Tree.children("docs/\n @red/\n - herr/\n", "docs").should == "@red/\n"
- # end
-
it "includes all sub-items when :include_subitems option" do
Tree.children("
- a/
@@ -771,7 +859,7 @@
".unindent
end
- it "includes all sub-items when no slashes" do
+ it "includes all items when no slashes and getting root" do
Tree.children("
- a
- b
@@ -785,6 +873,31 @@
".unindent
end
+ it "includes all sub-items when no slashes" do
+ Tree.children("
+ - a
+ - b
+ - c
+
+ - d
+ ", "a").should == "
+ - b
+ - c
+
+ - d
+ ".unindent
+ end
+
+ it "doesn't include sub-items of children with plus at beginning" do
+ Tree.children("
+ + a
+ + b
+ - c
+
+ - d
+ ", "a").should == "+ b".unindent
+ end
+
it "doesn't misinterpret blank lines as children" do
result = Tree.children "
- a/
@@ -885,6 +998,42 @@
options[:exclamations_args].should == nil
end
+ it "doesn't mess up when subpath and exclamations" do
+ Tree.children("
+ - right presentation/
+ ! aa
+ - right/
+ ! bb
+ ", ["right presentation"]).should == "! aa\n"
+ end
+
+
+
+ it "returns child of colon quoted items" do
+ Tree.children("
+ : a
+ : aa
+ : b
+ : ba
+ ", [": a"]).should == ": aa\n"
+ end
+
+ it "returns just one level of colon quoted items" do
+ Tree.children("
+ : a
+ : aa
+ : aaa
+ : b
+ ", [": a"]).should == ": aa\n"
+ end
+
+ it "finds child when hyphens" do
+ - what about when > file paths?"
+ Tree.children("
+ - foo-bar bah/
+ - ram/
+ ", "foo-bar-bah").should == "+ ram/\n"
+ end
end
@@ -1018,6 +1167,10 @@
Tree.target_match("a/b/c/", "a/b/z/").should == 2
end
+ it "doesn't confuse when partial match" do
+ Tree.target_match("right/! bb", "right presentation").should == 0
+ end
+
end
@@ -1037,68 +1190,6 @@
end
-describe Tree, "#to_html" do
- it "handles one tag" do
- Tree.to_html("
- p/
- hi
- ".unindent).should == "
-
- hi
-
- ".unindent
- end
-
- it "handles single with no contents" do
- Tree.to_html("
- p/
- ".unindent).should == "
-
-
- ".unindent
- end
-
- # it "doesn't close certain tags" do
- # Tree.to_html("
- # hr/
- # ".unindent).should == "
- #
- #
- # ".unindent
- # end
-
- it "doesn't confuse comments" do
- Tree.to_html("
- p/
- /* hey */
- ".unindent).should == "
-
- /* hey */
-
- ".unindent
- end
-
- it "adds closing tags to html" do
- Tree.to_html("
-
- hi
- ".unindent).should == "
-
- hi
-
- ".unindent
- end
-
- it "doesn't close comment tags" do
- Tree.to_html("
-
- hi
- ".unindent).should == "
-
- hi
- ".unindent
- end
-end
describe Tree, "#construct_path" do
before :each do
@@ -1129,12 +1220,26 @@
Tree.construct_path.should == "a/b/"
end
+ it "adds slash when last slash is escaped" do
+ # Simulates:
+ # a/
+ # b/
+
+ mock(Line).value.times(1) {" b/"}
+ mock(Line).value.times(1) {": a/"}
+ mock($el).search_backward_regexp(anything)
+
+ mock($el).goto_char(100)
+ Tree.construct_path.should == "a/b/"
+ end
+
+
it "stops when stop sign" do
# Simulates:
# a/
- # @b/
+ # =b/
- mock(Line).value.times(1) {" @b/"}
+ mock(Line).value.times(1) {" =b/"}
mock($el).goto_char(100)
Tree.construct_path.should == "b/"
end
@@ -1155,40 +1260,40 @@
it "returns a list when stop" do
# Simulates:
# a/
- # @b/
+ # =b/
- mock(Line).value.times(1) {" @b/"}
+ mock(Line).value.times(1) {" =b/"}
mock($el).search_backward_regexp(anything)
mock(Line).value.times(1) {"a/"}
mock($el).goto_char(100)
- Tree.construct_path(:all=>1, :list=>1).should == ["a/", "@b/"]
+ Tree.construct_path(:all=>1, :list=>1).should == ["a/", "=b/"]
end
it "handles when stop, slashes and list" do
# Simulates:
# a/
- # @b/
+ # =b/
- mock(Line).value.times(1) {" @b/"}
+ mock(Line).value.times(1) {" =b/"}
mock($el).search_backward_regexp(anything)
mock(Line).value.times(1) {"a/"}
mock($el).goto_char(100)
- Tree.construct_path(:all=>1, :slashes=>1).should == "a/@b/"
+ Tree.construct_path(:all=>1, :slashes=>1).should == "a/=b/"
end
it "leaves double slashes" do
# What should it return when this?...
# a//
- # @b/
+ # =b/
- mock(Line).value.times(1) {" @b/"}
+ mock(Line).value.times(1) {" =b/"}
mock($el).search_backward_regexp(anything)
mock(Line).value.times(1) {"a//"}
mock($el).goto_char(100)
- Tree.construct_path(:all=>1, :slashes=>1).should == "a//@b/"
+ Tree.construct_path(:all=>1, :slashes=>1).should == "a//=b/"
end
@@ -1202,22 +1307,77 @@
# b/@c/
# d/
end
+
+
+
+
+
+
+
+
+ it "appends hyphen to root when no slash" do
+ # Simulates:
+ # a
+
+ mock(Line).value.times(1) {"a"}
+ mock($el).goto_char(100)
+
+ Tree.construct_path.should == "a-"
+ end
+
+ it "appends hyphen to multiple roots when no slash" do
+ # Simulates:
+ # a
+ # = b
+
+ mock(Line).value.times(1) {" =b"}
+ mock($el).search_backward_regexp(anything)
+ mock(Line).value.times(1) {"a"}
+ mock($el).goto_char(100)
+
+ Tree.construct_path(:all=>1, :list=>1).should == ["a-", "=b-"]
+ end
+
+
+
+
end
describe Tree, "#join_to_subpaths" do
- it "leaves list boundaries only for at signs" do
- Tree.join_to_subpaths(["a/", "@b/", "c/"]).should == ["a/", "b/c/"]
+
+ it "leaves list boundaries only for equals signs" do
+ Tree.join_to_subpaths(["a/", "=b/", "c/"]).should == ["a/", "b/c/"]
+ end
+
+ it "doesn't split escaped equals sign" do
+ Tree.join_to_subpaths(["a/", "b/;=ip/"]).should == ["a/b/;=ip/"]
end
- it "it doesn't split escaped at sign" do
- Tree.join_to_subpaths(["a/", "b/;@ip/"]).should == ["a/b/;@ip/"]
+ it "doesn't split equals sign after escaped slash" do
+ Tree.join_to_subpaths(["a/", "b;/=ip/"]).should == ["a/b;/=ip/"]
end
- it "it doesn't split at sign after escaped slash" do
- Tree.join_to_subpaths(["a/", "b;/@ip/"]).should == ["a/b;/@ip/"]
+ it "doesn't split equals sign after escaped slash" do
+ Tree.join_to_subpaths(["a/", "b;/=ip/"]).should == ["a/b;/=ip/"]
end
- it "tests a bunch of other stuff, once we're comfortable with making this the official way of delimiting @'s and dealing with trailing slashes"
+ it "handles file path quotes" do
+ Tree.join_to_subpaths(["/foo/", "path.rb", "| def bar"]).should == ["/foo/path.rb/| def bar"]
+ end
+
+ it "doesn't turn blank items into slashes" do
+ Tree.join_to_subpaths(["", "ip/"]).should == ["ip/"]
+ end
+
+ it "merges single = without slashes" do
+ Tree.join_to_subpaths(["=", "ip/"]).should == ["ip/"]
+ end
+
+ it "splits when shell prompt" do
+ Tree.join_to_subpaths(["/dir/path/", "$ foo"]).should == ["/dir/path/", "$ foo"]
+ end
+
+ # it "tests a bunch of other stuff, once we're comfortable with making this the official way of delimiting @'s and dealing with trailing slashes"
# Paths we should handle:
# ["a/", "b/"].should == ["a/b/"]
@@ -1225,9 +1385,184 @@
# ["a/@b/"].should == ["a/", "b/"]
# ["/tmp/@b/"].should == ["/tmp/", "b/"]
# ["/tmp@b/"].should == ["/tmp", "b/"]
- # ["a", "b", "@c/", "d/", "p Tree.path_unified options={}"]
+ # ["a", "b", "@c/", "d/", "p Tree.path options={}"]
# Shouldn't split these:
# ["a@b/"]
+
+ it "adds slash delimiter even when escaped slash at end" do
+ Tree.join_to_subpaths(["a;/", "b"]).should == ["a;//b"]
+ end
+
end
+describe Tree, "#add_slashes_except_last" do
+ it "doesn't add slash to last" do
+ path = ["a", "b"]
+ Tree.add_slashes_except_last path
+ path.should == ["a/", "b"]
+ end
+
+ it "always adds slashes redundantly by default" do
+ # After unified, should probably remove this behavior, and make :only_if_needed not required
+ path = ["a/", "b"]
+ Tree.add_slashes_except_last path
+ path.should == ["a//", "b"]
+ end
+
+ it "doesn't add redundantly when :only_if_needed" do
+ # After unified, should probably remove this behavior, and make :only_if_needed not required
+ path = ["a/", "b"]
+ Tree.add_slashes_except_last path, :only_if_needed=>1
+ path.should == ["a/", "b"]
+ end
+
+ it "doesn't add slashes to blanks when :leave_blanks" do
+ path = ["", ""]
+ Tree.add_slashes_except_last path, :leave_blanks=>1
+ path.should == ["", ""]
+ end
+
+ it "adds slash when last is escaped" do
+ path = ["a;/", "b"]
+ Tree.add_slashes_except_last path, :only_if_needed=>1
+ path.should == ["a;//", "b"]
+ end
+
+end
+
+describe Tree, "#update" do
+ it "updates one item" do
+ txt = "
+ a/
+ b
+ c/
+ ".unindent
+ Tree.update(txt, ["a", "XX"]).should == "
+ a/
+ XX
+ c/
+ ".unindent
+ end
+
+ it "updates when bullets" do
+ txt = "
+ - a/
+ - b
+ - c/
+ ".unindent
+ Tree.update(txt, ["a", "XX"]).should == "
+ - a/
+ XX
+ - c/
+ ".unindent
+ end
+
+ it "updates multiple nested lines" do
+ txt = "
+ z/
+ a/
+ b
+ c
+ c/
+ ".unindent
+ Tree.update(txt, ["a", "XX\nYY"]).should == "
+ z/
+ a/
+ XX
+ YY
+ c/
+ ".unindent
+ end
+
+ it "returns when no match" do
+ txt = "a/"
+ Tree.update(txt, ["z", "XX"]).should == "a/"
+ end
+
+end
+
+
+describe Tree, "#matching_siblings_start_index" do
+ it "finds start" do
+ txt = "
+ * c/
+ a/
+ - b/
+ * c/
+ - b/
+ ".unindent.gsub(/^/, " ")
+ Tree.matching_siblings_start_index(txt, "* ").should == 21
+ end
+
+ it "finds when no indent" do
+ txt = "
+ a/
+ - b/
+ * c/
+ ".unindent
+ Tree.matching_siblings_start_index(txt, "* ").should == 10
+ end
+
+ it "returns 0 when not all match" do
+ txt = "
+ * a/
+ - b/
+ * c/
+ ".unindent
+ Tree.matching_siblings_start_index(txt, "* ").should == 0
+ end
+
+ it "returns length of all when no match" do
+ txt = "
+ a/
+ - b/
+ c/
+ ".unindent
+ Tree.matching_siblings_start_index(txt, "* ").should == 13
+ end
+
+ it "returns length of all when no match" do
+ txt = " - hi\n * bbb\n - hi\n * you\n * you\n"
+ Tree.matching_siblings_start_index(txt, "* ").should == 22
+ end
+
+end
+
+describe Tree, "#matching_siblings_end_index" do
+ it "finds nonmatching" do
+ txt = "
+ * a/
+ - b/
+ c/
+ ".unindent.gsub(/^/, " ")
+ Tree.matching_siblings_end_index(txt, "* ").should == 16
+ end
+
+ it "finds when no indent" do
+ txt = "
+ * a/
+ - b/
+ c/
+ ".unindent
+ Tree.matching_siblings_end_index(txt, "* ").should == 12
+ end
+
+ it "returns nil when not all match" do
+ txt = "
+ * a/
+ - b/
+ * c/
+ ".unindent
+ Tree.matching_siblings_end_index(txt, "* ").should == nil
+ end
+
+ it "returns 0 when none match" do
+ txt = "
+ a/
+ - b/
+ c/
+ ".unindent
+ Tree.matching_siblings_end_index(txt, "* ").should == 0
+ end
+end
diff --git a/spec/xik_spec.rb b/spec/xik_spec.rb
new file mode 100644
index 00000000..a6bf863c
--- /dev/null
+++ b/spec/xik_spec.rb
@@ -0,0 +1,228 @@
+$:.unshift "spec/"
+
+require './spec/spec_helper'
+class Keys
+ def self.prefix; nil; end
+end
+%w"ol path tree xik code".each {|o| require "xiki/core/#{o}"}
+%w"menu_handler".each {|o| require "xiki/handlers/#{o}"}
+
+describe Xik, ".expand" do
+ it "expands a" do
+ x = Xik.new "a/\n b/"
+ x["a"].should == "b/"
+ end
+
+ it "doesn't mess up when two times in a row" do
+ menu = Xik.new "
+ * a/
+ * b/
+ - c/
+ - d/
+ "
+ menu[''].should == "* a/\n* b/"
+ menu[''].should == "* a/\n* b/"
+ end
+
+ it "doesn't mess up pluses" do
+ x = Xik.new "
+ a/
+ b/
+ - c
+ - d
+ "
+ x["b"].should == "- c\n- d"
+ end
+
+ it "evals" do
+ x = Xik.new "
+ a/
+ ! 1 + 2
+ "
+ x["a", :eval=>1].should == "3"
+ end
+
+ it "adds :nest=>1 when :eval and wasn't code" do
+ options = {}
+ x = Xik.new "
+ a/
+ b/
+ "
+ x["a", :eval=>options].should == "b/"
+ options[:nest].should == 1
+Ol "options", options
+ end
+
+end
+
+describe Xik, ".parse_hash_or_array" do
+
+ it "handles hash" do
+ Xik.parse_hash_or_array({"a"=>"b"}).join("\n").should == "
+ - a/
+ - b/
+ ".unindent.strip
+ end
+
+ it "handles array" do
+ Xik.parse_hash_or_array(["a", "b"]).join("\n").should == "
+ - ./
+ - a/
+ - ./
+ - b/
+ ".unindent.strip
+ end
+
+ it "handles a hash of arrays" do
+ Xik.parse_hash_or_array({"a"=>["aa", "ab"], "b"=>"ba"}).join("\n").should == "
+ - a/
+ - ./
+ - aa/
+ - ./
+ - ab/
+ - b/
+ - ba/
+ ".unindent.strip
+ end
+
+end
+
+$:.unshift "spec/"
+
+require './spec/spec_helper'
+%w"ol path tree xik".each {|o| require "xiki/core/#{o}"}
+
+describe Xik, ".parse_hash_or_array" do
+
+ it "handles hash" do
+ Xik.parse_hash_or_array({"a"=>"b"}).join("\n").should == "
+ - a/
+ - b/
+ ".unindent.strip
+ end
+
+ it "handles array" do
+ Xik.parse_hash_or_array(["a", "b"]).join("\n").should == "
+ - ./
+ - a/
+ - ./
+ - b/
+ ".unindent.strip
+ end
+
+ it "handles hash of arrays" do
+ Xik.parse_hash_or_array({"a"=>["aa", "ab"], "b"=>"ba"}).join("\n").should == "
+ - a/
+ - ./
+ - aa/
+ - ./
+ - ab/
+ - b/
+ - ba/
+ ".unindent.strip
+ end
+end
+
+describe Xik, ".initialize" do
+ it "creates from string" do
+ x = Xik.new "
+ - a/
+ - b/
+ "
+ x.lines.should == ["- a/", " - b/", ""]
+ end
+
+ it "creates from hash of arrays" do
+ x = Xik.new "a"=>["aa"]
+ # x = Xik.new "
+ # - a/
+ # - ./
+ # - aa/
+ # "
+ x.lines.should == ["- a/", " - ./", " - aa/"]
+ end
+
+ it "keeps indent when :leave_indent" do
+ x = Xik.new " a/\n b/", :leave_indent=>1
+ x.lines.should == [" a/", " b/"]
+ end
+end
+
+describe Xik, ".cursor" do
+ it "starts at 0" do
+ x = Xik.foo
+ x.cursor.should == 1
+ end
+
+ it "works on 2nd line" do
+ x = Xik.foo
+ x.next
+ x.cursor.should == 4
+ end
+
+ it "works on 3rd line" do
+ x = Xik.foo
+ x.line = 3
+ x.cursor.should == 11
+ end
+
+ it "works when cursor not at axis" do
+ x = Xik.foo
+ x.line = 3
+ x.column = 3
+ x.cursor.should == 13
+ end
+
+end
+
+describe Xik, ".current" do
+ it "starts at 1st line" do
+ Xik.new("a\nb").current.should == "a"
+ end
+end
+
+describe Xik, ".line" do
+ it "starts at 1st line" do
+ Xik.new("a").line.should == 1
+ end
+end
+
+describe Xik, ".next" do
+ it "moves to 2nd line" do
+ x = Xik.new("a\nb")
+ x.next
+ x.line.should == 2
+ end
+
+ it "doesn't move past the file" do
+ x = Xik.new("a\nb")
+ x.line.should == 1
+ x.current.should == "a"
+
+ x.next
+ x.line.should == 2
+ x.current.should == "b"
+
+ x.next # Can't move past last line
+ x.line.should == 2
+ x.current.should == "b"
+ # x.line.should == 1
+ end
+end
+
+describe Xik, ".at_last_line?" do
+ it "works when blank" do
+ x = Xik.new("")
+ x.at_last_line?.should == true
+ end
+ it "works when no trailing and one line" do
+ x = Xik.new("a")
+ x.at_last_line?.should == true
+ end
+ it "works when one line" do
+ x = Xik.new("a\n")
+ x.at_last_line?.should == false
+ x.next
+ x.at_last_line?.should == true
+ end
+end
diff --git a/tests/console_test.rb b/tests/console_test.rb
index 729c7fba..e2a9cf38 100644
--- a/tests/console_test.rb
+++ b/tests/console_test.rb
@@ -5,7 +5,7 @@
class CodeTreeTest < Test::Unit::TestCase
def test_extract_method
- assert_equal("ssh foo@foo.org", Console.ssh_line("/foo@foo.org/"))
- assert_equal("ssh -p 1234 foo@foo.org", Console.ssh_line("foo@foo.org:1234"))
+ assert_equal("ssh foo@foo.org", Shell.ssh_line("/foo@foo.org/"))
+ assert_equal("ssh -p 1234 foo@foo.org", Shell.ssh_line("foo@foo.org:1234"))
end
end
diff --git a/xiki.gemspec b/xiki.gemspec
index 7c22d043..09054c7a 100644
--- a/xiki.gemspec
+++ b/xiki.gemspec
@@ -1,53 +1,111 @@
-# Generated by jeweler
-# DO NOT EDIT THIS FILE DIRECTLY
-# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
-Gem::Specification.new do |s|
- s.name = "xiki"
- s.version = "1.0.0a"
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Craig Muth"]
- s.date = "2013-05-20"
- s.executables = ["xiki"]
- s.summary = "A shell console with GUI features."
- s.description = "Xiki does what shell consoles do, but lets you edit everything at any time. It's trivial to make your own commands and menus to access other tools."
- s.email = "craig.muth@gmail.com"
- s.extra_rdoc_files = [
- "LICENSE",
- "README.markdown"
- ]
+# Xiki is temporarily not set up to be used as a gem. Just download it
+# and run bin/xsh directly.
+#
+# See comment in Gemfile for more details
- s.rdoc_options += %w[--exclude etc/templates/.*]
- files = `git ls-files`.split("\n")
- files = files.select{|o| o !~ /^etc\/xiki/}
- s.files = files
-
- s.homepage = "http://xiki.org"
- s.licenses = ["MIT"]
- s.require_paths = ["."]
-
- s.add_dependency('httparty')
- s.add_dependency('activesupport')
- s.add_dependency('method_source')
- s.add_dependency('net-ssh')
- s.add_dependency('net-scp')
- s.add_dependency('net-sftp')
- s.add_dependency('rake')
- s.add_dependency('rspec', [" ~> 2.12.0"])
- s.add_dependency('trogdoro-el4r', [">= 1.0.7"])
-
- s.add_dependency('sexp_processor')
- s.add_dependency('file-tail')
- s.add_dependency('ruby_parser')
- s.add_dependency('sourcify')
- s.add_dependency('daemons')
- s.add_dependency('simple-tidy')
- # s.add_dependency('nokogiri-pretty')
- s.add_dependency('sinatra')
-
- # Restore if changes get merged into main el4r gem
- # s.add_dependency('el4r')
-end
+#Gem::Specification.new do |s|
+# s.name = "xiki"
+# s.version = "2.0.1a"
+#
+# s.required_ruby_version = ">= 1.9.3"
+#
+# s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+# s.authors = ["Craig Muth"]
+# s.date = "2014-09-28"
+#
+#"
+#/projects/xiki/
+# - README.md
+# : - wait, maybe just move loop into different file?
+# : - if I don't have Gemfile:_gemspec, it probably won't do the binary?
+# : - try just hard-coding the Gemfile with ''gem 'foo' files''
+#"
+#
+## If not, this should fix it
+##
+##- Maybe instructions should be for doing "bundle install" in subdir?
+## - So, making the directions be
+## | $ cd $xiki_dir/install
+## | $ bundle install
+## - look at what they are now!
+## - and move dependencies out into a 3rd file?
+## - loop through and do "s.add_dependency" or "gem" from Gemfile and .gemspec?
+#
+#
+# # Check out what the methods on "s" are
+# # It might clue in whether running from bundler
+#
+# # Try removing s.executables...
+# # to see if that'll still cause it to add it to the path
+# # when "$ bundle install"
+#
+# if ENV['_'] !~ /\/bundle$/ # If not called by bundler, install executables
+# s.executables = ["xiki"]
+# end
+#
+#
+#
+#
+#
+#
+# s.summary = "A shell console with GUI features."
+# s.description = "Xiki does what shell consoles do, but lets you edit everything at any time. It's trivial to make your own commands and menus to access other tools."
+# s.email = "craig.muth@gmail.com"
+# s.extra_rdoc_files = [
+# "LICENSE",
+# "README.md"
+# ]
+#
+# s.rdoc_options += %w[--exclude misc/templates/.*]
+#
+# files = `git ls-files`.split("\n")
+# files = files.select{|o| o !~ /^etc\/xiki/}
+# s.files = files
+#
+# s.homepage = "http://xiki.org"
+# s.licenses = ["MIT"]
+# s.require_paths = ["."]
+# s.require_paths = ["lib"]
+#
+# # s.add_dependency('awesome_print')
+# # s.add_dependency('json')
+# # s.add_dependency('httparty')
+# # s.add_dependency('activesupport')
+## s.add_dependency('erubis')
+# #s.add_dependency('method_source')
+# #s.add_dependency('net-ssh')
+# #s.add_dependency('net-scp')
+# #s.add_dependency('net-sftp')
+#
+# # s.add_dependency('rake')
+# # s.add_dependency('rspec', [" ~> 2.12.0"])
+# # s.add_dependency('trogdoro-el4r', [">= 1.0.10"])
+#
+## s.add_dependency('sexp_processor')
+# # s.add_dependency('file-tail')
+# # s.add_dependency('ruby_parser')
+# # s.add_dependency('sourcify')
+# # s.add_dependency('daemons')
+# # s.add_dependency('simple-tidy')
+# # s.add_dependency('nokogiri-pretty')
+# # s.add_dependency('sinatra')
+# # s.add_dependency('session') # For keeping a bash session open to send shell commands to
+# # s.add_dependency('map') # For keeping a bash session open to send shell commands to
+#
+# # Restore if changes get merged into main el4r gem
+# # s.add_dependency('el4r')
+#
+#
+#
+#
+#
+# # TODO > put instructions here "like 'run the xiki command in the shell console"
+# s.post_install_message = "Thanks for installing Xiki.\n\nInstructions\n\nRun the 'xiki' command in a shell console. It will walk you through getting started."
+#
+# # Make the 'xiki' command say to start the web server?
+#
+#end