xiki 0.5.0a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/README.markdown +83 -0
- data/Rakefile +8 -0
- data/bin/xiki +46 -0
- data/etc/command/xiki_command.rb +203 -0
- data/etc/command/xiki_process.rb +52 -0
- data/etc/command/xiki_wrapper +2 -0
- data/etc/js/menu1.js +55 -0
- data/etc/js/xiki.js +259 -0
- data/etc/logo.png +0 -0
- data/etc/presentations/bootstrap.deck +5 -0
- data/etc/presentations/databases.deck +41 -0
- data/etc/presentations/diffs.deck +23 -0
- data/etc/presentations/documentation.deck +14 -0
- data/etc/presentations/effects.deck +5 -0
- data/etc/presentations/face.deck +297 -0
- data/etc/presentations/files.deck +79 -0
- data/etc/presentations/icons.deck +22 -0
- data/etc/presentations/images.deck +24 -0
- data/etc/presentations/key_shortcuts.deck +16 -0
- data/etc/presentations/macros.deck +18 -0
- data/etc/presentations/menu_classes.deck +44 -0
- data/etc/presentations/menu_directories.deck +30 -0
- data/etc/presentations/notes.deck +19 -0
- data/etc/presentations/other_languages.deck +55 -0
- data/etc/presentations/other_wiki_syntaxes.deck +4 -0
- data/etc/presentations/piano.deck +5 -0
- data/etc/presentations/potential/diffs.deck +38 -0
- data/etc/presentations/potential/evolution.deck +18 -0
- data/etc/presentations/potential/intro.deck +711 -0
- data/etc/presentations/potential/intro1.deck +711 -0
- data/etc/presentations/potential/intro2.deck +97 -0
- data/etc/presentations/potential/make_mysql_menu.deck +36 -0
- data/etc/presentations/potential/ruby_development.deck +17 -0
- data/etc/presentations/potential/ui_prototyping.deck +50 -0
- data/etc/presentations/potential/web_dev.deck +4 -0
- data/etc/presentations/potential/web_development.deck +10 -0
- data/etc/presentations/potential/wiki.deck +45 -0
- data/etc/presentations/presentations.deck +24 -0
- data/etc/presentations/rails_development.deck +29 -0
- data/etc/presentations/search_key_shortcuts.deck +37 -0
- data/etc/presentations/simplest_possible_ui.deck +37 -0
- data/etc/presentations/svg.deck +5 -0
- data/etc/presentations/testing.deck +28 -0
- data/etc/presentations/the_end.deck +13 -0
- data/etc/presentations/type_something_and_double_click.deck +57 -0
- data/etc/presentations/type_the_acronym.deck +144 -0
- data/etc/presentations/web_browser.deck +56 -0
- data/etc/presentations/xiki_command.deck +20 -0
- data/etc/presentations/xiki_url.deck +14 -0
- data/etc/shark.icns +0 -0
- data/etc/shark_script.icns +0 -0
- data/etc/snippets/html.notes +7 -0
- data/etc/snippets/notes.notes +20 -0
- data/etc/snippets/rb.notes +38 -0
- data/etc/templates/menu_template.menu +2 -0
- data/etc/templates/menu_template.rb +8 -0
- data/etc/templates/template.rb +5 -0
- data/etc/themes/Dark_Metal.notes +28 -0
- data/etc/themes/Orange_Path.notes +15 -0
- data/etc/themes/Shiny_Blue.notes +27 -0
- data/etc/themes/Shiny_Green.notes +27 -0
- data/etc/wrappers/wrapper.js +17 -0
- data/etc/wrappers/wrapper.py +20 -0
- data/etc/wrappers/wrapper.rb +25 -0
- data/lib/block.rb +72 -0
- data/lib/bookmarks.rb +352 -0
- data/lib/buffers.rb +170 -0
- data/lib/clipboard.rb +333 -0
- data/lib/code.rb +860 -0
- data/lib/code_tree.rb +476 -0
- data/lib/color.rb +274 -0
- data/lib/console.rb +557 -0
- data/lib/control_lock.rb +9 -0
- data/lib/control_tab.rb +176 -0
- data/lib/core_ext.rb +31 -0
- data/lib/cursor.rb +111 -0
- data/lib/deletes.rb +65 -0
- data/lib/diff_log.rb +297 -0
- data/lib/effects.rb +145 -0
- data/lib/environment.rb +5 -0
- data/lib/file_tree.rb +1875 -0
- data/lib/files.rb +334 -0
- data/lib/hide.rb +259 -0
- data/lib/history.rb +286 -0
- data/lib/image.rb +51 -0
- data/lib/incrementer.rb +15 -0
- data/lib/insert.rb +7 -0
- data/lib/irc.rb +22 -0
- data/lib/key_bindings.rb +658 -0
- data/lib/keys.rb +754 -0
- data/lib/launcher.rb +1351 -0
- data/lib/line.rb +429 -0
- data/lib/links.rb +6 -0
- data/lib/location.rb +175 -0
- data/lib/macros.rb +48 -0
- data/lib/man.rb +19 -0
- data/lib/menu.rb +708 -0
- data/lib/merb.rb +259 -0
- data/lib/message.rb +5 -0
- data/lib/meths.rb +56 -0
- data/lib/mode.rb +34 -0
- data/lib/move.rb +248 -0
- data/lib/notes.rb +1000 -0
- data/lib/numbers.rb +45 -0
- data/lib/ol.rb +203 -0
- data/lib/ol_helper.rb +44 -0
- data/lib/overlay.rb +167 -0
- data/lib/pause_means_space.rb +68 -0
- data/lib/php.rb +22 -0
- data/lib/projects.rb +21 -0
- data/lib/relinquish_exception.rb +2 -0
- data/lib/remote.rb +206 -0
- data/lib/requirer.rb +46 -0
- data/lib/rest_tree.rb +108 -0
- data/lib/ruby.rb +57 -0
- data/lib/ruby_console.rb +165 -0
- data/lib/search.rb +1572 -0
- data/lib/search_term.rb +40 -0
- data/lib/snippet.rb +68 -0
- data/lib/specs.rb +229 -0
- data/lib/styles.rb +274 -0
- data/lib/svn.rb +682 -0
- data/lib/text_util.rb +110 -0
- data/lib/tree.rb +1871 -0
- data/lib/tree_cursor.rb +87 -0
- data/lib/trouble_shooting.rb +27 -0
- data/lib/url_tree.rb +11 -0
- data/lib/view.rb +1474 -0
- data/lib/window.rb +133 -0
- data/lib/xiki.rb +404 -0
- data/menus/accounts.rb +5 -0
- data/menus/address_book.rb +21 -0
- data/menus/agenda.rb +28 -0
- data/menus/all.rb +5 -0
- data/menus/amazon.rb +16 -0
- data/menus/app.rb +16 -0
- data/menus/applescript.rb +46 -0
- data/menus/as.rb +15 -0
- data/menus/bookmarklet.rb +63 -0
- data/menus/bootstrap.rb +568 -0
- data/menus/browse.rb +13 -0
- data/menus/browser.rb +78 -0
- data/menus/cassandra_db.rb +36 -0
- data/menus/chmod.rb +27 -0
- data/menus/classes.rb +5 -0
- data/menus/coffee_script.rb +35 -0
- data/menus/computer.rb +24 -0
- data/menus/contacts.rb +5 -0
- data/menus/cookies.rb +25 -0
- data/menus/couch.rb +184 -0
- data/menus/crop.rb +45 -0
- data/menus/css.rb +55 -0
- data/menus/current.rb +5 -0
- data/menus/db.rb +12 -0
- data/menus/deck.rb +219 -0
- data/menus/dictionary.rb +9 -0
- data/menus/dir.rb +8 -0
- data/menus/disk.rb +5 -0
- data/menus/do.rb +13 -0
- data/menus/docs.rb +58 -0
- data/menus/dotsies.rb +107 -0
- data/menus/edited.rb +18 -0
- data/menus/emacs.rb +17 -0
- data/menus/enter.rb +13 -0
- data/menus/eval.rb +17 -0
- data/menus/executable.rb +16 -0
- data/menus/filter.rb +46 -0
- data/menus/firefox.rb +607 -0
- data/menus/foo.rb +30 -0
- data/menus/french.rb +7 -0
- data/menus/git.rb +185 -0
- data/menus/gito.rb +785 -0
- data/menus/google.rb +35 -0
- data/menus/google_images.rb +11 -0
- data/menus/google_patents.rb +10 -0
- data/menus/gutenberg.rb +13 -0
- data/menus/head.rb +10 -0
- data/menus/headings.rb +39 -0
- data/menus/html.rb +61 -0
- data/menus/icon.rb +40 -0
- data/menus/images.menu +2 -0
- data/menus/img.rb +15 -0
- data/menus/info.rb +9 -0
- data/menus/ip.rb +10 -0
- data/menus/iterm.rb +36 -0
- data/menus/itunes.rb +78 -0
- data/menus/javascript.rb +74 -0
- data/menus/layout.rb +18 -0
- data/menus/local_storage.rb +67 -0
- data/menus/ls.rb +19 -0
- data/menus/mac.rb +87 -0
- data/menus/maps.rb +18 -0
- data/menus/matches.rb +18 -0
- data/menus/memcache.rb +117 -0
- data/menus/mkdir.rb +23 -0
- data/menus/mongo.rb +83 -0
- data/menus/mysql.rb +294 -0
- data/menus/node.rb +88 -0
- data/menus/open.rb +19 -0
- data/menus/outline.rb +24 -0
- data/menus/piano.rb +746 -0
- data/menus/postgres.rb +34 -0
- data/menus/pre.rb +5 -0
- data/menus/python.rb +39 -0
- data/menus/rails.rb +160 -0
- data/menus/rake.rb +12 -0
- data/menus/redmine.rb +168 -0
- data/menus/riak_tree.rb +204 -0
- data/menus/rss.rb +15 -0
- data/menus/safari.rb +11 -0
- data/menus/sass.rb +15 -0
- data/menus/say.rb +6 -0
- data/menus/scale.rb +49 -0
- data/menus/serve.rb +78 -0
- data/menus/shuffle.rb +24 -0
- data/menus/spanish.rb +7 -0
- data/menus/standalone.rb +57 -0
- data/menus/tail.rb +41 -0
- data/menus/technologies.rb +19 -0
- data/menus/themes.rb +32 -0
- data/menus/thesaurus.rb +13 -0
- data/menus/to.rb +24 -0
- data/menus/twitter.rb +57 -0
- data/menus/wikipedia.rb +34 -0
- data/menus/words.rb +11 -0
- data/spec/code_tree_spec.rb +59 -0
- data/spec/diff_log_spec.rb +40 -0
- data/spec/file_tree_spec.rb +102 -0
- data/spec/keys_spec.rb +24 -0
- data/spec/line_spec.rb +68 -0
- data/spec/menu_spec.rb +50 -0
- data/spec/ol_spec.rb +98 -0
- data/spec/remote_spec.rb +43 -0
- data/spec/search_spec.rb +162 -0
- data/spec/text_util_spec.rb +119 -0
- data/spec/tree_cursor_spec.rb +91 -0
- data/spec/tree_spec.rb +955 -0
- data/tests/console_test.rb +11 -0
- data/tests/couch_db_test.rb +12 -0
- data/tests/diff_log_test.rb +43 -0
- data/tests/el_mixin.rb +16 -0
- data/tests/git_test.rb +95 -0
- data/tests/keys_test.rb +19 -0
- data/tests/line_test.rb +38 -0
- data/tests/merb_test.rb +11 -0
- data/tests/redmine_test.rb +50 -0
- data/tests/remote_test.rb +31 -0
- data/tests/rest_tree_test.rb +70 -0
- data/xiki.gemspec +37 -0
- metadata +332 -0
data/lib/effects.rb
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require 'styles'
|
|
2
|
+
|
|
3
|
+
# Makes visual things happen
|
|
4
|
+
class Effects
|
|
5
|
+
|
|
6
|
+
def self.menu
|
|
7
|
+
"
|
|
8
|
+
> Do cool-looking things to text
|
|
9
|
+
- api/
|
|
10
|
+
| Try these out by double-clicking on them.
|
|
11
|
+
- Glow/
|
|
12
|
+
@Effects.glow
|
|
13
|
+
@Effects.glow :times=>6
|
|
14
|
+
@Effects.glow :what=>:paragraph
|
|
15
|
+
@Effects.glow :what=>:block
|
|
16
|
+
@Effects.glow :what=>[1, 100]
|
|
17
|
+
|
|
18
|
+
- Colors/
|
|
19
|
+
@Effects.glow :color=>:fire
|
|
20
|
+
@Effects.glow :color=>:water
|
|
21
|
+
@Effects.glow :color=>:forest
|
|
22
|
+
@Effects.glow :color=>:rainbow
|
|
23
|
+
@Effects.glow :color=>:fat_rainbow
|
|
24
|
+
- Fade in and out/
|
|
25
|
+
@Effects.glow :fade_in=>1
|
|
26
|
+
@Effects.glow :fade_out=>1
|
|
27
|
+
- Blink/
|
|
28
|
+
| Makes line blink orange. Using a longer time since the blink happens
|
|
29
|
+
| anyway.
|
|
30
|
+
|
|
31
|
+
@Effects.blink :time=>1
|
|
32
|
+
- Some View methods that use effects/
|
|
33
|
+
@View.prompt
|
|
34
|
+
@View.flash
|
|
35
|
+
@View.flash 'Saved!'
|
|
36
|
+
- docs/
|
|
37
|
+
> Keys
|
|
38
|
+
| do+line+effects: make line blink
|
|
39
|
+
| up+do+line+effects: make line blink rainbow color
|
|
40
|
+
|
|
41
|
+
> See
|
|
42
|
+
<< themes/
|
|
43
|
+
"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.glow options={}
|
|
47
|
+
|
|
48
|
+
what = options[:what]
|
|
49
|
+
if what.is_a? Array
|
|
50
|
+
left, right = what
|
|
51
|
+
elsif what == :block
|
|
52
|
+
ignore, left, right = View.block_positions "^[|>]"
|
|
53
|
+
elsif what == :paragraph
|
|
54
|
+
left, right = View.paragraph :bounds=>1
|
|
55
|
+
else
|
|
56
|
+
left, right = Line.left, Line.right
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Set :times to 1 if no args and fade out
|
|
61
|
+
times = 1 if ! options[:times] && (options[:fade_out] || options[:fade_in])
|
|
62
|
+
|
|
63
|
+
times ||= options[:times] || 3
|
|
64
|
+
|
|
65
|
+
over = $el.make_overlay left, right
|
|
66
|
+
|
|
67
|
+
faces =
|
|
68
|
+
if options[:color] == :fire
|
|
69
|
+
['font-lock-doc-face', 'font-lock-function-name-face', 'html-helper-server-script-face', 'font-lock-variable-name-face', 'speedbar-tag-face', 'speedbar-tag-face', 'speedbar-tag-face']
|
|
70
|
+
elsif options[:color] == :forest
|
|
71
|
+
['dired-mark', 'dired-mark', 'widget-documentation', 'widget-documentation', 'widget-documentation', 'bookmark-menu-heading', 'bookmark-menu-heading']
|
|
72
|
+
elsif options[:color] == :water
|
|
73
|
+
['font-lock-constant-face', 'font-lock-constant-face', 'escape-glyph', 'escape-glyph', 'font-lock-builtin-face', 'font-lock-builtin-face', 'font-lock-builtin-face']
|
|
74
|
+
elsif options[:color] == :rainbow
|
|
75
|
+
['blue', 'red', 'orange', 'orange', 'green', 'green', 'yellow']
|
|
76
|
+
elsif options[:color] == :fat_rainbow
|
|
77
|
+
['notes-yellow', 'notes-yellow', 'notes-green', 'notes-green', 'notes-blue', 'notes-red', 'notes-red']
|
|
78
|
+
else
|
|
79
|
+
['fade1', 'fade2', 'fade3', 'fade4', 'fade5', 'fade6', 'fade7']
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
up = [6, 5, 4, 3, 2]
|
|
83
|
+
down = [2, 3, 4, 5, 6]
|
|
84
|
+
|
|
85
|
+
sequence =
|
|
86
|
+
if options[:fade_out]; [1] + (down + [7]) * times
|
|
87
|
+
elsif options[:fade_in]; [7] + (up + [1]) * times
|
|
88
|
+
elsif options[:reverse]; [7] + (up + [1] + down + [7]) * times
|
|
89
|
+
else; [1] + (down + [7] + up + [1]) * times
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
sequence.each do |i|
|
|
93
|
+
$el.overlay_put over, :face, (faces[i-1] || faces[0])
|
|
94
|
+
$el.sit_for 0.02
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
$el.delete_overlay over
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Sample usages:
|
|
101
|
+
def self.blink options={}
|
|
102
|
+
what = options[:what]
|
|
103
|
+
what ||= :line
|
|
104
|
+
case what
|
|
105
|
+
when :all
|
|
106
|
+
left, right = View.top, View.bottom
|
|
107
|
+
when :region
|
|
108
|
+
left, right = View.range
|
|
109
|
+
when :line
|
|
110
|
+
left, right = Line.bounds
|
|
111
|
+
right += 1
|
|
112
|
+
when :sexp
|
|
113
|
+
left, right = bounds_of_thing_at_point(:sexp).to_a
|
|
114
|
+
return unless left
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
left = options[:left] if options[:left]
|
|
118
|
+
right = options[:right] if options[:right]
|
|
119
|
+
|
|
120
|
+
time = options[:time] || 0.04
|
|
121
|
+
over2 = $el.make_overlay(left, right)
|
|
122
|
+
$el.overlay_put over2, :face, :color_rb_glow2
|
|
123
|
+
$el.sit_for time
|
|
124
|
+
$el.delete_overlay over2
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Define font
|
|
128
|
+
def self.define_styles
|
|
129
|
+
Styles.define :color_rb_glow1, :bg => "fec"
|
|
130
|
+
Styles.define :color_rb_glow2, :bg => "f90"
|
|
131
|
+
|
|
132
|
+
Styles.define :red, :fg => "f00"
|
|
133
|
+
Styles.define :orange, :fg => "f80"
|
|
134
|
+
Styles.define :yellow, :fg => "ff0"
|
|
135
|
+
Styles.define :green, :fg => "0f0"
|
|
136
|
+
Styles.define :blue, :fg => "00f"
|
|
137
|
+
Styles.define :indigo, :fg => "408"
|
|
138
|
+
Styles.define :violet, :fg => "82e"
|
|
139
|
+
|
|
140
|
+
Styles.define :purple, :fg => "808"
|
|
141
|
+
Styles.define :cyan, :fg => "f0f"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
Effects.define_styles
|
data/lib/environment.rb
ADDED
data/lib/file_tree.rb
ADDED
|
@@ -0,0 +1,1875 @@
|
|
|
1
|
+
require 'styles'
|
|
2
|
+
require 'line'
|
|
3
|
+
require 'view'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'cursor'
|
|
7
|
+
|
|
8
|
+
# Draws a tree from a dir structure and lets you incrementally search in the tree.
|
|
9
|
+
# Usage (user):
|
|
10
|
+
# - Type C-x C-t
|
|
11
|
+
# - A tree will be drawn in a new buffer
|
|
12
|
+
# - Representing the directory structure in the current dir
|
|
13
|
+
# - Type Enter to open a file
|
|
14
|
+
# - Navigate within the buffer
|
|
15
|
+
# - Then press Enter when you're on the file you wish to open
|
|
16
|
+
# Usage (api):
|
|
17
|
+
# - Call this to draw a tree from the current directory
|
|
18
|
+
# - FileTree.ls
|
|
19
|
+
class FileTree
|
|
20
|
+
|
|
21
|
+
@@one_view_in_bar_by_default = false
|
|
22
|
+
|
|
23
|
+
def self.menu
|
|
24
|
+
%`
|
|
25
|
+
- docs/
|
|
26
|
+
| > Summary
|
|
27
|
+
| Lets you navigate your filesystem as a tree.
|
|
28
|
+
|
|
|
29
|
+
| Type a file path on a line and double-click on it. Here's an example
|
|
30
|
+
| (the "@" isn't required when there are no spaces at the beginning of the
|
|
31
|
+
| line).
|
|
32
|
+
|
|
|
33
|
+
@/
|
|
34
|
+
|
|
|
35
|
+
- overview of keys/
|
|
36
|
+
| When you launch a path, the cursor turns blue and you go into a temporary
|
|
37
|
+
| search. Here are some keys you can type:
|
|
38
|
+
|
|
|
39
|
+
| - Letters: search and filter lines
|
|
40
|
+
|
|
|
41
|
+
| - Return: stops searching and launches (expands file or dir)
|
|
42
|
+
| - Tab: like return but hides others
|
|
43
|
+
| - ;: like return but collapses path
|
|
44
|
+
|
|
|
45
|
+
| - C-g: stops searching
|
|
46
|
+
|
|
|
47
|
+
- api/
|
|
48
|
+
| Turn paths into a tree
|
|
49
|
+
@ puts Tree.paths_to_tree $el.elvar.recentf_list.to_a[0..2]
|
|
50
|
+
`
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# TODO
|
|
54
|
+
# - Make search handle trees with multiple roots
|
|
55
|
+
|
|
56
|
+
# Call this method from your init.rb to use the default key shortcuts.
|
|
57
|
+
def self.keys
|
|
58
|
+
Keys.XT { FileTree.ls }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize
|
|
62
|
+
@res = ""
|
|
63
|
+
@list = []
|
|
64
|
+
@file_regex = nil
|
|
65
|
+
end
|
|
66
|
+
attr :res
|
|
67
|
+
attr :list
|
|
68
|
+
attr_accessor :file_regex
|
|
69
|
+
|
|
70
|
+
# Change dirs into spaces, etc
|
|
71
|
+
def clean path, indent=0
|
|
72
|
+
path = path.gsub(/.+?\//, " ")
|
|
73
|
+
indent == 0 ?
|
|
74
|
+
path :
|
|
75
|
+
path.sub(/^#{indent}/, '')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Recursively draws out tree
|
|
79
|
+
def traverse path
|
|
80
|
+
entries = Dir.glob("#{View.expand_path(path)}/*", File::FNM_DOTMATCH).
|
|
81
|
+
select {|i| i !~ /\/\.(\.*|svn|git)$/}. # Exclude some dirs (exclude entensions here too?)
|
|
82
|
+
sort
|
|
83
|
+
|
|
84
|
+
# Process dirs
|
|
85
|
+
entries.each{ |f|
|
|
86
|
+
next unless FileTest.directory?(f)
|
|
87
|
+
cleaned = clean f
|
|
88
|
+
@res += "#{cleaned.sub(/(^ *)/, "\\1- ")}/\n"
|
|
89
|
+
traverse f
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Process files
|
|
93
|
+
entries.each{ |f|
|
|
94
|
+
next unless FileTest.file?(f)
|
|
95
|
+
cleaned = clean f
|
|
96
|
+
@res += "#{cleaned.sub(/(^ *)/, "\\1+ ")}\n"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.grep dir, regex, options={}
|
|
102
|
+
|
|
103
|
+
raw = options[:raw]
|
|
104
|
+
|
|
105
|
+
# Expand out bookmark (if there is one)
|
|
106
|
+
dir = Bookmarks.expand(dir)
|
|
107
|
+
dir = Bookmarks.dir_only dir # Cut off file (if there is one)
|
|
108
|
+
dir.sub!(/\/$/, '') # Remove slash from end
|
|
109
|
+
|
|
110
|
+
# Turn regex into a regex, if a string
|
|
111
|
+
regex = Regexp.new(regex, Regexp::IGNORECASE) if regex.is_a? String
|
|
112
|
+
|
|
113
|
+
unless raw
|
|
114
|
+
View.bar if options[:bar]
|
|
115
|
+
View.to_buffer "*tree grep"; View.dir = dir
|
|
116
|
+
View.clear; Notes.mode
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
@@indent_count = dir.count('/') - 1
|
|
120
|
+
@@indent = " " * @@indent_count
|
|
121
|
+
t = self.new
|
|
122
|
+
|
|
123
|
+
files = options[:files]
|
|
124
|
+
if files
|
|
125
|
+
files = Regexp.new(files, Regexp::IGNORECASE) if files.is_a? String
|
|
126
|
+
t.file_regex = files
|
|
127
|
+
end
|
|
128
|
+
t.list << "- #{dir}/"
|
|
129
|
+
t.grep_inner dir, regex, :first_time
|
|
130
|
+
|
|
131
|
+
list = t.list
|
|
132
|
+
Tree.clear_empty_dirs! list
|
|
133
|
+
if raw
|
|
134
|
+
return list
|
|
135
|
+
end
|
|
136
|
+
if list.size == 0
|
|
137
|
+
View.insert "> Note\n- No Results Found!\n"
|
|
138
|
+
else
|
|
139
|
+
View.insert(list.join("\n") + "\n")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
View.to_top
|
|
143
|
+
$el.highlight_regexp(regex, :ls_quote_highlight) if regex
|
|
144
|
+
$el.re_search_forward "|"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.grep_with_hashes path, regex, prepend='##'
|
|
148
|
+
Search.append_log path, "- #{prepend}#{regex}/"
|
|
149
|
+
|
|
150
|
+
View.to_buffer "*tree grep"
|
|
151
|
+
View.dir = Files.dir_of(path)
|
|
152
|
+
View.clear; Notes.mode
|
|
153
|
+
|
|
154
|
+
if File.directory?(path)
|
|
155
|
+
View << "- #{File.expand_path path}/\n - #{prepend}#{regex}/\n"
|
|
156
|
+
else
|
|
157
|
+
dir, name = path.match(/(.+\/)(.+)/)[1..2]
|
|
158
|
+
View << "- #{File.expand_path dir}/\n - #{name}\n - #{prepend}#{regex}/\n"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
View.to_bottom; Line.previous
|
|
162
|
+
self.launch
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def self.grep_one_file(f, regex, indent)
|
|
166
|
+
result = []
|
|
167
|
+
IO.foreach(f) do |line|
|
|
168
|
+
line.gsub!(/[\r\n\c@]+/, '')
|
|
169
|
+
if regex
|
|
170
|
+
next unless line =~ regex
|
|
171
|
+
end
|
|
172
|
+
result << "#{indent}| #{line}"
|
|
173
|
+
end
|
|
174
|
+
result
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def self.outline_search(f, regex, indent)
|
|
178
|
+
result = []
|
|
179
|
+
current_line = Search.outline_goto_once # If search_outline, we want to put cursor on that line when done
|
|
180
|
+
line_found, matches_count, i = nil, 0, 0
|
|
181
|
+
IO.foreach(f) do |line|
|
|
182
|
+
i+=1
|
|
183
|
+
line.gsub!(/[\r\n\c@]+/, '')
|
|
184
|
+
|
|
185
|
+
if current_line && line_found.nil?
|
|
186
|
+
line_found = matches_count if i == current_line
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
if regex
|
|
190
|
+
next unless line =~ regex
|
|
191
|
+
end
|
|
192
|
+
result << "#{indent}| #{line}"
|
|
193
|
+
matches_count+=1
|
|
194
|
+
end
|
|
195
|
+
Search.outline_goto_once = line_found
|
|
196
|
+
result
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def self.skip
|
|
200
|
+
@skip || {}
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def grep_inner path, regex, first_time=nil
|
|
204
|
+
|
|
205
|
+
path.sub!(/\/$/, '')
|
|
206
|
+
entries = Dir["#{path}/*"].entries.sort
|
|
207
|
+
|
|
208
|
+
entries = entries.select{|o| o !~ /\/(vendor|log)$/} # Exclude some dirs (why doing it here?
|
|
209
|
+
|
|
210
|
+
# Exclude some file extensions and dirs
|
|
211
|
+
# Make these be in config! pass along as instance var
|
|
212
|
+
# TMP:::: Hard-code skipping image/ dirs (for now) !!!!!!!!!!!!!
|
|
213
|
+
entries = entries.select{|o| o !~ /\/(images|twilio_sound)$/}
|
|
214
|
+
|
|
215
|
+
if first_time and skip = FileTree.skip[path]
|
|
216
|
+
entries = entries.select{|o| ! skip.member? o[/.+\/(.+)/, 1]}
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Process dirs
|
|
220
|
+
entries.each{ |f|
|
|
221
|
+
next unless FileTest.directory?(f)
|
|
222
|
+
cleaned = clean(f, @@indent)
|
|
223
|
+
@list << "#{cleaned.sub(/(^ *)/, "\\1- ")}/"
|
|
224
|
+
grep_inner f, regex
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
indent = nil
|
|
228
|
+
|
|
229
|
+
# Process files
|
|
230
|
+
entries.each do |f|
|
|
231
|
+
next unless FileTest.file?(f)
|
|
232
|
+
|
|
233
|
+
# If matching filename, skip if no match
|
|
234
|
+
if file_regex
|
|
235
|
+
stem = f[/[^\/]+$/]
|
|
236
|
+
next unless stem =~ file_regex
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
indent = " " * (f.count('/') - @@indent_count) unless indent
|
|
240
|
+
|
|
241
|
+
if regex
|
|
242
|
+
result = FileTree.grep_one_file(f, regex, indent) # Search in file contents
|
|
243
|
+
|
|
244
|
+
if result.size > 0 # Add if any files were found
|
|
245
|
+
@list << clean(f, @@indent).sub(/(^ *)/, "\\1- ")
|
|
246
|
+
@list += result
|
|
247
|
+
end
|
|
248
|
+
else
|
|
249
|
+
@list << clean(f, @@indent).sub(/(^ *)/, "\\1+ ")
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Does ls in current buffer, without making any modifications to the environment
|
|
256
|
+
def self.ls_here dir
|
|
257
|
+
t = self.new
|
|
258
|
+
t.traverse View.dir
|
|
259
|
+
View.insert "- #{dir}\n"
|
|
260
|
+
|
|
261
|
+
result_indent = t.res[/^ */] # Get indent of first line
|
|
262
|
+
View.insert t.res.gsub(/^#{result_indent}/, " ")
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def self.define_styles
|
|
267
|
+
|
|
268
|
+
return if ! $el
|
|
269
|
+
|
|
270
|
+
if Styles.inverse # Bullets
|
|
271
|
+
Styles.define :ls_bullet,
|
|
272
|
+
:face => 'courier', :size => "+2", # Mac
|
|
273
|
+
:fg => "dd7700", :bold => true
|
|
274
|
+
else
|
|
275
|
+
Styles.define :ls_bullet,
|
|
276
|
+
:face => 'courier', :size => "+2", # Mac
|
|
277
|
+
:fg => "ff7700", :bold => true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
if Styles.inverse
|
|
282
|
+
Styles.define :quote_heading, :fg=>"fff", :size=>"0", :face=>"arial", :bold=>false
|
|
283
|
+
Styles.define :quote_heading2, :fg=>"fff", :size=>"-2", :face=>"arial", :bold=>false
|
|
284
|
+
Styles.define :quote_heading_pipe, :fg=>"333", :size=>"0", :face => "verdana", :bold=>true
|
|
285
|
+
Styles.define :quote_heading_bracket, :fg=>"4c4c4c", :size=>"-2", :face => "Monaco", :bold=>true
|
|
286
|
+
Styles.define :quote_heading_small, :fg=>"fff", :size=>"-2", :face => "arial black", :bold=>true
|
|
287
|
+
|
|
288
|
+
Styles.define :diff_line_number, :bold => true, :size => "-2", :fg => "444444"
|
|
289
|
+
Styles.define :diff_red, :bg => "400", :fg => "ee3333", :size => "-1"
|
|
290
|
+
Styles.define :diff_green, :bg => "130", :fg => "44dd33", :size => "-1"
|
|
291
|
+
Styles.define :diff_small, :fg => "222", :size => "-11"
|
|
292
|
+
|
|
293
|
+
Styles.tree_keys :fg=>"#fff", :underline=>nil
|
|
294
|
+
|
|
295
|
+
# dir/
|
|
296
|
+
Styles.define :ls_dir, :fg => "888", :face => "verdana", :size => "-1", :bold => true
|
|
297
|
+
# Styles.define :ls_dir, :fg => "bbb", :face => "verdana", :size => "-2", :bold => true
|
|
298
|
+
|
|
299
|
+
else
|
|
300
|
+
Styles.define :quote_heading, :fg=>"444", :size=>"0", :face=>"arial", :bold=>false
|
|
301
|
+
Styles.define :quote_heading2, :fg=>"aaa", :size=>"-2", :face=>"arial", :bold=>false
|
|
302
|
+
Styles.define :quote_heading_pipe, :fg=>"bbb", :size=>"0", :face => "verdana", :bold=>true
|
|
303
|
+
Styles.define :quote_heading_bracket, :fg=>"bbb", :size=>"-2", :face => "Monaco", :bold=>true
|
|
304
|
+
Styles.define :quote_heading_small, :fg=>"fff", :size=>"-2", :face => "arial black", :bold=>true
|
|
305
|
+
|
|
306
|
+
Styles.define :diff_line_number, :bold => true, :size => "-2", :fg => "ccc"
|
|
307
|
+
Styles.define :diff_red, :bg => "ffdddd", :fg => "cc4444"
|
|
308
|
+
Styles.define :diff_green, :bg => "ddffcc", :fg => "337744"
|
|
309
|
+
Styles.define :diff_small, :fg => "ddd", :size => "-11"
|
|
310
|
+
|
|
311
|
+
Styles.tree_keys :fg=>"#ff0", :underline=>1
|
|
312
|
+
|
|
313
|
+
# dir/
|
|
314
|
+
Styles.define :ls_dir, :fg => "777", :face => "verdana", :size => "-1", :bold => true
|
|
315
|
+
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# ##search/
|
|
319
|
+
Styles.define :ls_search,
|
|
320
|
+
:fg => "ff7700",
|
|
321
|
+
:face => "verdana",
|
|
322
|
+
:size => "-2",
|
|
323
|
+
:bold => true
|
|
324
|
+
|
|
325
|
+
if Styles.inverse # | Quoted text
|
|
326
|
+
Styles.define :ls_quote,
|
|
327
|
+
:size => "-1",
|
|
328
|
+
:fg => "aaa"
|
|
329
|
+
else
|
|
330
|
+
Styles.define :ls_quote,
|
|
331
|
+
:size => "-1",
|
|
332
|
+
:fg => "777"
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# 001| Quoted text lines
|
|
336
|
+
Styles.define :ls_quote_line_number,
|
|
337
|
+
:size => "-4",
|
|
338
|
+
:fg => "eee"
|
|
339
|
+
|
|
340
|
+
# Highlight in search
|
|
341
|
+
Styles.define :ls_quote_highlight,
|
|
342
|
+
:size => "-1",
|
|
343
|
+
:bg => "ffff44",
|
|
344
|
+
:fg => "666666"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def self.apply_styles
|
|
348
|
+
$el.el4r_lisp_eval "(setq font-lock-defaults '(nil t))"
|
|
349
|
+
|
|
350
|
+
# Must go before quotes - if it goes after, it supercedes them
|
|
351
|
+
Styles.apply("\\(~\\)\\(.+?\\)\\(~\\)", nil, :quote_heading_bracket, :notes_label, :quote_heading_bracket)
|
|
352
|
+
|
|
353
|
+
Styles.apply("https?://[a-zA-Z0-9\/.~_:-]+", :notes_link) # blue-ify url's
|
|
354
|
+
|
|
355
|
+
# - bullets
|
|
356
|
+
Styles.apply("^[ \t]*\\([+=-]\\)\\( \\)", nil, :ls_bullet, :variable)
|
|
357
|
+
|
|
358
|
+
# With numbers
|
|
359
|
+
Styles.apply("^ +\\(:[0-9]+\\)\\(|.*\n\\)", nil, :ls_quote_line_number, :ls_quote)
|
|
360
|
+
|
|
361
|
+
# Path-like lines and parts of lines (make gray)
|
|
362
|
+
|
|
363
|
+
# Single "@" at beginning
|
|
364
|
+
Styles.apply("^[ <+=@-]*\\(@\\)", nil, :ls_dir) # @
|
|
365
|
+
|
|
366
|
+
Styles.apply("^[ \t]*\\(<+=?@?\\)\\( \\)", nil, :ls_bullet, :variable)
|
|
367
|
+
|
|
368
|
+
# Remove later?
|
|
369
|
+
Styles.apply("^[ <+=@-]*\\([^|\n]+/\\)$", nil, :ls_dir) # slash at end
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# Slash after almost anything
|
|
373
|
+
|
|
374
|
+
Styles.apply("^[ <+-]*@?\\([~$#a-zA-Z0-9_,? ().:-]*[^ \n]\/\\)", nil, :ls_dir)
|
|
375
|
+
# Covers paths in files by themselves
|
|
376
|
+
|
|
377
|
+
Styles.apply("^[ <+-]*\\([@~$a-zA-Z0-9_,? ().:<-]*\/[@\#'$a-zA-Z0-9_,? ().:\/<-]+\/\\)", nil, :ls_dir) # Paths with multiple slashes
|
|
378
|
+
|
|
379
|
+
Styles.apply("^[ \t]*[<+-] [a-zA-Z0-9_,? ().:-]+?[:)] \\(\[.@a-zA-Z0-9 ]+\/\\)", nil, :ls_dir) # label, one word, slash
|
|
380
|
+
Styles.apply("^[ \t]*[<+-] [a-zA-Z0-9_,? ().:-]+?[:)] \\([.@a-zA-Z0-9 ]+\/[.@a-zA-Z0-9 \/]+\/\\)", nil, :ls_dir) # label, one word, path, slash
|
|
381
|
+
|
|
382
|
+
# Bullets
|
|
383
|
+
Styles.apply("^[ \t]*[+-] [^(\n]+?) \\(.+/\\)$", nil, :ls_dir) # - hey) /what/
|
|
384
|
+
Styles.apply("^[ \t]*[+-] [a-zA-Z0-9_,? ().:-]+?: \\(.+/\\)$", nil, :ls_dir) # - hey: /what/
|
|
385
|
+
|
|
386
|
+
# Put this one back?
|
|
387
|
+
# Styles.apply("^[ +-]*\\([^|\n]+/\\)$", nil, :ls_dir) # Dirs with bullets
|
|
388
|
+
|
|
389
|
+
Styles.apply('https?://[a-zA-Z0-9\/.~_:?%&=|+!-#-]+', :notes_link) # Url
|
|
390
|
+
|
|
391
|
+
# |... lines (quotes)
|
|
392
|
+
Styles.apply("^ *\\(|\\)\\( *\\)", nil, :quote_heading_pipe, :ls_quote)
|
|
393
|
+
Styles.apply("^ *\\(|\\)\\(.*\n\\)", nil, :quote_heading_pipe, :ls_quote)
|
|
394
|
+
Styles.apply("^ *\\(|\\)\\(.+?\\)([+-].*[-+])", nil, :quote_heading_pipe, :ls_quote) # quoted lines: beginnings of lines
|
|
395
|
+
Styles.apply("^ *|.*([-+].*[+-])\\(.+\\)$", nil, :ls_quote) # quoted lines: ends of lines
|
|
396
|
+
Styles.apply("[+-])\\(.*?\\)([+-]", nil, :ls_quote) # quoted lines: between diffs
|
|
397
|
+
|
|
398
|
+
# | >... headings
|
|
399
|
+
Styles.apply("^ *\\(|\\)\\( \\)\\(>\\)\\(\n\\| .*\n\\)", nil, :quote_heading_pipe, :ls_quote, :quote_heading_bracket, :quote_heading)
|
|
400
|
+
Styles.apply("^ *\\(|\\)\\( \\)\\(>>\\)\\(\n\\| .*\n\\)", nil, :quote_heading_pipe, :ls_quote, :quote_heading_bracket, :quote_heading_small)
|
|
401
|
+
|
|
402
|
+
# >... headings (indented)
|
|
403
|
+
Styles.apply("^ +\\(> ?\\)\\(\n\\|.*\n\\)", nil, :quote_heading_bracket, :quote_heading)
|
|
404
|
+
|
|
405
|
+
# >>... headings (indented)
|
|
406
|
+
Styles.apply("^ +\\(>>\\)\\(\n\\| .*\n\\)", nil, :quote_heading_bracket, :quote_heading2)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# |+... diffs
|
|
410
|
+
Styles.apply("^ +\\(:[0-9]+\\)$", nil, :ls_quote)
|
|
411
|
+
Styles.apply("^ *\\(|\\+.*\\)", nil, :diff_green) # whole lines
|
|
412
|
+
Styles.apply("^ *\\(|\-.*\\)", nil, :diff_red)
|
|
413
|
+
Styles.apply("^ *\\(|@@ .*\n\\)", nil, :diff_line_number)
|
|
414
|
+
|
|
415
|
+
#Styles.apply('^[ -]*\\([ a-zA-Z0-9\/_\.$-]*\\w/\\)$', nil, :ls_dir) # Most dirs
|
|
416
|
+
Styles.apply('^ *\\(//?\\)$', nil, :ls_dir) # /
|
|
417
|
+
Styles.apply('^ *\\(\./\\)$', nil, :ls_dir) # ./
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def self.apply_styles_at_end
|
|
421
|
+
Styles.apply('^ *[+-] \\(##.*/\\)$', nil, :ls_search) # ##_/
|
|
422
|
+
Styles.apply('^ *\\([+-] \\)?\\(@f/.*/\\)$', nil, nil, :ls_search) # ##_/
|
|
423
|
+
Styles.apply('^ *[+-] \\(\*\*.+/\\)$', nil, :ls_search) # **_/
|
|
424
|
+
Styles.apply('^ *\\([+-] \\)?\\(@n/.*/\\)$', nil, nil, :ls_search) # ##_/
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def self.handles? list=nil
|
|
428
|
+
begin
|
|
429
|
+
list ||= Tree.construct_path(:list=>true) # Use current line by default
|
|
430
|
+
rescue Exception=>e
|
|
431
|
+
return nil
|
|
432
|
+
end
|
|
433
|
+
return 0 if self.matches_root_pattern?(Line.without_label :line=>list.first)
|
|
434
|
+
nil
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def self.matches_root_pattern? item
|
|
438
|
+
item =~ /^\/(\w|$)/ || # /... or /
|
|
439
|
+
item =~ /^~\// ||
|
|
440
|
+
item =~ /^\.\.?\// ||
|
|
441
|
+
item =~ /^\$\w/
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Open the line in the tree that the cursor is on. This is probably
|
|
445
|
+
# be mapped to C-. .
|
|
446
|
+
# TODO: remove ignore_prefix, and just use Keys.clear_prefix
|
|
447
|
+
def self.open options={}
|
|
448
|
+
|
|
449
|
+
# If passed just a dir, open it in new view
|
|
450
|
+
return self.ls :dir=>options if options.is_a? String
|
|
451
|
+
|
|
452
|
+
original_file = View.file_name
|
|
453
|
+
|
|
454
|
+
path = options[:path] || Tree.construct_path(:list=>true)
|
|
455
|
+
path_orig = path
|
|
456
|
+
|
|
457
|
+
if path.is_a?(Array)
|
|
458
|
+
# Pull off search string if exists
|
|
459
|
+
search_string = path.pop[/\|(.*)/, 1] if path.last =~ /^\|/
|
|
460
|
+
search_string.sub! /^[ +-]/, '' if search_string # Should start with space or -|+
|
|
461
|
+
# Discard rest of |... lines
|
|
462
|
+
path = path.grep(/^[^|]/).join('')
|
|
463
|
+
else
|
|
464
|
+
# Split off |... if it's there (search string)
|
|
465
|
+
path =~ /(.*?)-?\+?\|(.*)/
|
|
466
|
+
path, search_string = $1, $2 if $2
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Pull number off end of path if there
|
|
470
|
+
path =~ /(.+):(\d+)$/
|
|
471
|
+
path, line_number = $1, $2 if $2
|
|
472
|
+
|
|
473
|
+
path = Bookmarks.expand(path)
|
|
474
|
+
|
|
475
|
+
return Files.open_in_os(path) if Keys.prefix == 0
|
|
476
|
+
|
|
477
|
+
remote = self.is_remote?(path)
|
|
478
|
+
unless remote
|
|
479
|
+
path = File.expand_path(path)
|
|
480
|
+
end
|
|
481
|
+
# Prefix keys with specific behavior
|
|
482
|
+
prefix_was_u = false
|
|
483
|
+
|
|
484
|
+
prefix = Keys.prefix
|
|
485
|
+
|
|
486
|
+
case prefix
|
|
487
|
+
when :u # Just open file
|
|
488
|
+
prefix_was_u = true
|
|
489
|
+
Keys.clear_prefix
|
|
490
|
+
when "update" # Save ($ave) file
|
|
491
|
+
return self.save_quoted path
|
|
492
|
+
when "delete" # Save ($ave) file
|
|
493
|
+
return self.delete_file
|
|
494
|
+
when "all"
|
|
495
|
+
Keys.clear_prefix
|
|
496
|
+
search_string ? # If quote, enter lines under
|
|
497
|
+
Tree.enter_under :
|
|
498
|
+
self.enter_lines(//) # If file, enter all lines
|
|
499
|
+
return
|
|
500
|
+
else
|
|
501
|
+
if prefix =~ /\boutline\b/
|
|
502
|
+
prefix = Keys.prefix_n
|
|
503
|
+
Keys.clear_prefix
|
|
504
|
+
return self.drill_quotes_or_enter_lines path, search_string, :prefix=>prefix
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# If numeric prefix, jump to nth window
|
|
509
|
+
if (! options[:ignore_prefix]) and Keys.prefix_n and Keys.prefix != 7
|
|
510
|
+
|
|
511
|
+
# If number larger than number of windows, open new one first
|
|
512
|
+
if Keys.prefix_n > View.list.size
|
|
513
|
+
View.to_nth(View.list.size - 1)
|
|
514
|
+
View.create
|
|
515
|
+
end
|
|
516
|
+
View.to_nth(Keys.prefix_n - 1)
|
|
517
|
+
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
column = View.column
|
|
521
|
+
if search_string
|
|
522
|
+
column -= (Line.value[/^.*?\|./] || '').length
|
|
523
|
+
column = 0 if column < 0
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Open or go to file
|
|
527
|
+
|
|
528
|
+
if remote
|
|
529
|
+
self.remote_file_contents(path) # Get text from server and insert
|
|
530
|
+
|
|
531
|
+
else # Normal file opening
|
|
532
|
+
|
|
533
|
+
# See if it exists, and store contents if not?
|
|
534
|
+
|
|
535
|
+
options[:same_view] ? View.open(path, :same_view=>true) : Location.go(path)
|
|
536
|
+
Effects.blink(:what=>:line) unless line_number || search_string || path =~ /\.xiki$/
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
return unless line_number || search_string
|
|
540
|
+
|
|
541
|
+
if line_number # If line number, go to it
|
|
542
|
+
$el.goto_line line_number.to_i
|
|
543
|
+
Effects.blink(:what=>:line)
|
|
544
|
+
elsif search_string # Else, search for |... string if it passed
|
|
545
|
+
Hide.reveal if View.hidden?
|
|
546
|
+
|
|
547
|
+
Move.top
|
|
548
|
+
# Search for exact line match
|
|
549
|
+
found = Search.forward "^#{$el.regexp_quote(search_string)}$"
|
|
550
|
+
|
|
551
|
+
unless found # If not found, search for substring of line, but with a break at the end
|
|
552
|
+
Move.top
|
|
553
|
+
# :beginning
|
|
554
|
+
found = Search.forward "#{$el.regexp_quote(search_string)}\\([^_a-zA-Z0-9\n]\\|$\\)", :beginning=>true
|
|
555
|
+
# found = $el.search_forward_regexp("#{$el.regexp_quote(search_string)}\\([^_a-zA-Z0-9\n]\\|$\\)", nil, true)
|
|
556
|
+
end
|
|
557
|
+
unless found # If not found, search for substring of line
|
|
558
|
+
Move.top
|
|
559
|
+
found = $el.search_forward_regexp "#{$el.regexp_quote(search_string)}", nil, true
|
|
560
|
+
end
|
|
561
|
+
unless found # If not found, search for it stripped
|
|
562
|
+
Move.top
|
|
563
|
+
found = $el.search_forward_regexp "#{$el.regexp_quote(search_string.strip)}", nil, true
|
|
564
|
+
end
|
|
565
|
+
unless found # If not found, suggest creating or show error
|
|
566
|
+
|
|
567
|
+
return if self.suggest_creating search_string
|
|
568
|
+
|
|
569
|
+
View.beep
|
|
570
|
+
View.message "Didn't find: \"#{search_string.strip}\"", :beep=>1
|
|
571
|
+
return
|
|
572
|
+
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
$el.beginning_of_line
|
|
576
|
+
$el.recenter(0) unless prefix_was_u
|
|
577
|
+
Effects.blink(:what=>:line)
|
|
578
|
+
|
|
579
|
+
dir, name = path.match(/(.+\/)(.+)/)[1..2]
|
|
580
|
+
|
|
581
|
+
return if original_file == "search_log.notes"
|
|
582
|
+
|
|
583
|
+
# Add to log
|
|
584
|
+
Search.append_log dir, "- #{name}\n | #{search_string}"
|
|
585
|
+
end
|
|
586
|
+
View.column = column
|
|
587
|
+
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def self.suggest_creating search_string
|
|
591
|
+
|
|
592
|
+
if search_string =~ /^ *def self\.(.+)/ # If it's a method, suggest creating it
|
|
593
|
+
Code.suggest_creating_method View.file, $1
|
|
594
|
+
return true
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
return false # We didn't handle it
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def self.drill_quotes_or_enter_lines path, quote, options={}
|
|
602
|
+
return self.enter_lines(nil, options) if ! quote # If not quote must be whole file
|
|
603
|
+
result = self.drill_quote path # Try showing only children with children
|
|
604
|
+
return Tree.<<(result) if result.any?
|
|
605
|
+
return Tree.enter_under # Only leafs, so show them
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def self.save_quoted path
|
|
609
|
+
txt = Tree.siblings :quotes=>1
|
|
610
|
+
|
|
611
|
+
if txt[0] !~ /^\|/
|
|
612
|
+
# TODO: also check that it's a tree
|
|
613
|
+
# return View.flash("- All siblings should be quoted - (make this more flexible?)")
|
|
614
|
+
return self.move_or_delete_via_diffs
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
dir = File.dirname path
|
|
618
|
+
|
|
619
|
+
if ! File.exists? dir
|
|
620
|
+
View.flash "- Dir doesn\'t exist. Create it?"
|
|
621
|
+
|
|
622
|
+
key = Keys.input :chars=>1, :prompt=>"Create the \"#{dir}\" directory? "
|
|
623
|
+
return if key != "y"
|
|
624
|
+
|
|
625
|
+
`mkdir -p "#{dir}"`
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
txt = txt.map{|o| "#{o.sub /^\| ?/, ''}\n"}.join('')
|
|
629
|
+
DiffLog.save_diffs :patha=>path, :textb=>txt
|
|
630
|
+
|
|
631
|
+
File.open(path, "w") { |f| f << txt }
|
|
632
|
+
|
|
633
|
+
View.flash "- Saved!"
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def self.operations_via_diffs added, deleted
|
|
637
|
+
tmp_path = "/tmp/saved.txt"
|
|
638
|
+
operations = [[], []]
|
|
639
|
+
|
|
640
|
+
file = View.file
|
|
641
|
+
$el.with(:with_temp_buffer) do
|
|
642
|
+
$el.insert_file file
|
|
643
|
+
# Ol << "View.txt(1, 150): #{View.txt(1, 150)}"
|
|
644
|
+
deleted.each do |line_number|
|
|
645
|
+
View.line = line_number
|
|
646
|
+
path = Tree.construct_path
|
|
647
|
+
operations[1] << path
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
if added.any?
|
|
652
|
+
$el.with(:with_temp_buffer) do
|
|
653
|
+
$el.insert_file tmp_path
|
|
654
|
+
added.each do |add|
|
|
655
|
+
line_number = add
|
|
656
|
+
View.line = line_number
|
|
657
|
+
path = Tree.construct_path
|
|
658
|
+
operations[0] << path
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
operations
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
def self.move_or_delete_via_diffs
|
|
666
|
+
|
|
667
|
+
# Save current version to temporary place, and diff
|
|
668
|
+
tmp_path = "/tmp/saved.txt"
|
|
669
|
+
$el.write_region nil, nil, tmp_path
|
|
670
|
+
diff = Console.sync %`diff --old-group-format="d%df %dn%c'\012'" --new-group-format="a%dF %dN%c'\012'" --unchanged-line-format="" "#{View.file}" "#{tmp_path}"`
|
|
671
|
+
|
|
672
|
+
added, deleted = DiffLog.parse_tree_diffs diff
|
|
673
|
+
|
|
674
|
+
return Tree.<<("> Error\n| You haven't changed any lines (since you last saved).", :no_slash=>1) if added.blank? && deleted.blank?
|
|
675
|
+
|
|
676
|
+
return Tree.<<("> Error\n| You\'ve deleted #{deleted.length} lines but added #{added.length} (since you last saved).\n| It\'s unclear what you\'re trying to do.", :no_slash=>1) if added.any? && added.length != deleted.length
|
|
677
|
+
operation = added.any? ? :moves : :deletes
|
|
678
|
+
|
|
679
|
+
operations = self.operations_via_diffs added, deleted
|
|
680
|
+
|
|
681
|
+
View.flash "- Are you sure?", :times=>1
|
|
682
|
+
|
|
683
|
+
if operation == :moves
|
|
684
|
+
message = "Move these files?\n"
|
|
685
|
+
operations[0].each_with_index do |o, i|
|
|
686
|
+
delete = operations[1][i]
|
|
687
|
+
message << "- #{delete} -> #{o}\n"
|
|
688
|
+
end
|
|
689
|
+
else
|
|
690
|
+
message = "Delete these files?\n"
|
|
691
|
+
operations[1].each do |o|
|
|
692
|
+
message << "- #{o}\n"
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
choice = Keys.input :prompt=>"#{message}", :chars=>1
|
|
697
|
+
|
|
698
|
+
return if choice !~ /[ym]/i
|
|
699
|
+
|
|
700
|
+
if operation == :moves
|
|
701
|
+
operations[0].each_with_index do |o, i|
|
|
702
|
+
delete = operations[1][i]
|
|
703
|
+
command = "mv \"#{delete}\" \"#{o}\""
|
|
704
|
+
Console.sync command, :dir=>"/tmp/"
|
|
705
|
+
end
|
|
706
|
+
else
|
|
707
|
+
operations[1].each do |o|
|
|
708
|
+
command = "rm \"#{o}\""
|
|
709
|
+
Console.sync command, :dir=>"/tmp/"
|
|
710
|
+
end
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
View.flash "- done!", :times=>1
|
|
714
|
+
|
|
715
|
+
nil
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def self.drill_quote path
|
|
719
|
+
parent = Line.value
|
|
720
|
+
parent.sub! /^ *\| /, ''
|
|
721
|
+
|
|
722
|
+
found, indent, candidate, result = false, 0, nil, ""
|
|
723
|
+
IO.foreach(path) do |line|
|
|
724
|
+
line = line[/.*/]
|
|
725
|
+
|
|
726
|
+
if ! found
|
|
727
|
+
if line == parent
|
|
728
|
+
found = true
|
|
729
|
+
indent = (line[/^ */].length / 2) + 1
|
|
730
|
+
end
|
|
731
|
+
next
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# Found
|
|
735
|
+
|
|
736
|
+
# Skip if blank
|
|
737
|
+
next if line.blank?
|
|
738
|
+
|
|
739
|
+
current_indent = line[/^ */].length / 2
|
|
740
|
+
|
|
741
|
+
# If indented exactly 2 under, add it
|
|
742
|
+
if current_indent == indent
|
|
743
|
+
candidate = "| #{line}\n"
|
|
744
|
+
|
|
745
|
+
# If child of candidate, append candidate if one needs appending
|
|
746
|
+
elsif current_indent == indent + 1
|
|
747
|
+
next if candidate.nil?
|
|
748
|
+
result << candidate
|
|
749
|
+
candidate = nil
|
|
750
|
+
# If indented less, stop
|
|
751
|
+
elsif current_indent < indent
|
|
752
|
+
break
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
result
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
# Goes through files in reverse order and deletes empty dirs
|
|
760
|
+
def self.select_next_file
|
|
761
|
+
$el.search_forward_regexp /[^\/]$/
|
|
762
|
+
$el.beginning_of_line
|
|
763
|
+
$el.skip_chars_forward " "
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
def self.select_previous_file
|
|
767
|
+
$el.search_backward_regexp /[^\/]$/
|
|
768
|
+
$el.beginning_of_line
|
|
769
|
+
$el.skip_chars_forward "\t"
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
# Draw tree, use dir of bookmark from user input
|
|
773
|
+
def self.ls options={}
|
|
774
|
+
dir = options[:dir]
|
|
775
|
+
# If no dir, do tree in current dir
|
|
776
|
+
dir ||= $el.elvar.default_directory
|
|
777
|
+
|
|
778
|
+
line = Line.value
|
|
779
|
+
|
|
780
|
+
Line << "\n" if line =~ /^>/ # If it's on a >... line, insert linebreak
|
|
781
|
+
|
|
782
|
+
# If to be in bar, go to the tree file in the bar
|
|
783
|
+
if options[:open_in_bar]
|
|
784
|
+
self.open_in_bar :ignore_prefix # Open tree
|
|
785
|
+
# If not on a blank line, go to the top
|
|
786
|
+
View.to_top unless line =~ /^$/
|
|
787
|
+
# If on line, add another blank
|
|
788
|
+
unless line =~ /^$/
|
|
789
|
+
View.insert "\n\n"; backward_char 2
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
dir = View.expand_path(dir) # Expand out ~
|
|
793
|
+
# If file, back up to dir
|
|
794
|
+
dir = Bookmarks.dir_only dir unless File.directory?(dir)
|
|
795
|
+
|
|
796
|
+
# If dir or recursive
|
|
797
|
+
if File.directory?(dir) || options[:recursive]
|
|
798
|
+
dir.sub!(/([^\/])$/, "\\1/") # Add slash on end if not there
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
name = dir[/.+\/(.+)\//, 1]
|
|
802
|
+
|
|
803
|
+
# Don't open new dir if not appropriate
|
|
804
|
+
if options[:open_in_bar] || options[:here]
|
|
805
|
+
# Don't upen buffer
|
|
806
|
+
else
|
|
807
|
+
View.to_buffer "*tree #{name}"
|
|
808
|
+
View.clear
|
|
809
|
+
View.dir = Bookmarks.dir_only dir
|
|
810
|
+
Notes.mode
|
|
811
|
+
$el.use_local_map $el.elvar.notes_mode_map
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# If recursive
|
|
815
|
+
if options[:recursive]
|
|
816
|
+
left = $el.point
|
|
817
|
+
self.ls_here dir # Draw actual tree
|
|
818
|
+
right = $el.point
|
|
819
|
+
$el.goto_char left
|
|
820
|
+
self.select_next_file
|
|
821
|
+
# Start incremental search
|
|
822
|
+
Tree.search(:recursive => true, :left => left, :right => right)
|
|
823
|
+
#self.draw(dir)
|
|
824
|
+
else
|
|
825
|
+
# Insert with linebreak if file
|
|
826
|
+
if File.file?(dir)
|
|
827
|
+
#insert dir
|
|
828
|
+
View.insert self.filename_to_next_line(dir) # Add linebreak before filename
|
|
829
|
+
#insert dir.sub(/(.+)\//, "\\1/\n ") # Add linebreak before filename
|
|
830
|
+
open_line 1
|
|
831
|
+
#Tree.search
|
|
832
|
+
Line.to_words
|
|
833
|
+
Tree.search(:left => Line.left, :right => Line.left(2))
|
|
834
|
+
return
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
bullet = "+ "
|
|
838
|
+
|
|
839
|
+
bullet = "" if line =~ /^[ @]+$/
|
|
840
|
+
bullet = "@" if line =~ /^ +$/
|
|
841
|
+
|
|
842
|
+
View.insert "#{bullet}#{dir}\n"
|
|
843
|
+
$el.previous_line
|
|
844
|
+
self.dir options # Draw actual tree
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def self.open_in_bar options={}
|
|
850
|
+
prefix = Keys.prefix :clear=>1
|
|
851
|
+
|
|
852
|
+
# If numeric prefix, open nth thing in tree
|
|
853
|
+
if prefix and prefix != :u and !(options[:ignore_prefix])
|
|
854
|
+
View.flash "- Don't know what this was supposed to do!"
|
|
855
|
+
|
|
856
|
+
# Remember original view
|
|
857
|
+
# start = $el.selected_window
|
|
858
|
+
# Open tree (ignoring prefix)
|
|
859
|
+
# self.open_in_bar# :ignore_prefix=>true
|
|
860
|
+
# Find nth file in tree
|
|
861
|
+
# View.to_highest
|
|
862
|
+
|
|
863
|
+
# prefix.times do
|
|
864
|
+
# re_search_forward "^ +[a-zA-Z0-9_.-]+$"
|
|
865
|
+
# end
|
|
866
|
+
# Go to next line if comment
|
|
867
|
+
|
|
868
|
+
# Line.next if Line.next_matches(/^ *\|/)
|
|
869
|
+
# Line.to_beginning
|
|
870
|
+
# self.open :ignore_prefix=>true
|
|
871
|
+
return
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
unless View.bar? # If already open, just go there
|
|
875
|
+
View.bar
|
|
876
|
+
end
|
|
877
|
+
View.to_nth 0
|
|
878
|
+
$el.find_file Bookmarks.expand("$t")
|
|
879
|
+
|
|
880
|
+
only_one_view_in_bar = prefix == :u
|
|
881
|
+
only_one_view_in_bar = ! only_one_view_in_bar if @@one_view_in_bar_by_default
|
|
882
|
+
|
|
883
|
+
unless only_one_view_in_bar # Unless u prefix, open $tl as well (under bar)
|
|
884
|
+
|
|
885
|
+
# If 2nd view isn't at left margin, open 2nd view
|
|
886
|
+
if View.left_edge(View.list[1]) != 0
|
|
887
|
+
View.create
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
View.to_nth 1
|
|
891
|
+
|
|
892
|
+
# If 2nd view isn't $f, open it
|
|
893
|
+
if $el.buffer_file_name( $el.window_buffer( View.list[1] ) ) != Bookmarks["$f"]
|
|
894
|
+
$el.find_file Bookmarks["$f"]
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
View.to_nth 0
|
|
898
|
+
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# Creates tree snippet of text in file
|
|
903
|
+
def self.snippet options={}
|
|
904
|
+
txt = options[:txt] || View.selection
|
|
905
|
+
file = options[:file] || View.file
|
|
906
|
+
|
|
907
|
+
# Remove linebreak from end
|
|
908
|
+
txt = txt.sub(/\n\z/, "")
|
|
909
|
+
if file
|
|
910
|
+
txt = "#{File.dirname(file)}/\n - #{File.basename(file)}\n" +
|
|
911
|
+
txt.gsub(/^/, " | ").
|
|
912
|
+
gsub(/^ \| $/, " |") # Remove trailing spaces on blank lines
|
|
913
|
+
"#{txt}\n"
|
|
914
|
+
else
|
|
915
|
+
# "- From #{buffer_name}:\n" + txt.gsub(/^/, " #")
|
|
916
|
+
"- From #{View.name}:\n" + txt.gsub(/^/, " | ")
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
# Recursively display dir in tree # Insert dir contents at point (usually in existing tree)
|
|
921
|
+
def self.dir options={}
|
|
922
|
+
return Files.open_in_os(Tree.construct_path) if Keys.prefix == 0
|
|
923
|
+
|
|
924
|
+
Tree.plus_to_minus_maybe
|
|
925
|
+
Line.to_left
|
|
926
|
+
prefix = Keys.prefix
|
|
927
|
+
if prefix == 8 || prefix == "all"
|
|
928
|
+
self.dir_recursive
|
|
929
|
+
elsif prefix == "delete"
|
|
930
|
+
return self.delete_file
|
|
931
|
+
else
|
|
932
|
+
self.dir_one_level options
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
# Insert all files in dirs within a dir, and allow user to
|
|
937
|
+
# incremental search therein.
|
|
938
|
+
def self.dir_recursive
|
|
939
|
+
$el.beginning_of_line
|
|
940
|
+
line = Line.value
|
|
941
|
+
left = $el.point
|
|
942
|
+
indent = line[/^ */] + " " # Get indent
|
|
943
|
+
dir = Tree.construct_path
|
|
944
|
+
|
|
945
|
+
Line.next
|
|
946
|
+
|
|
947
|
+
# Get tree
|
|
948
|
+
t = self.new
|
|
949
|
+
dir.sub!(/\/$/, '')
|
|
950
|
+
t.traverse Bookmarks.expand(dir)
|
|
951
|
+
|
|
952
|
+
# Adjust indent
|
|
953
|
+
result_indent = t.res[/^ */] # Get indent of first line
|
|
954
|
+
View.insert t.res.gsub(/^#{result_indent}/, indent)
|
|
955
|
+
|
|
956
|
+
right = $el.point
|
|
957
|
+
|
|
958
|
+
$el.goto_char left
|
|
959
|
+
# isearch_forward
|
|
960
|
+
self.select_next_file
|
|
961
|
+
Tree.search(:recursive => true, :left => left, :right => right)
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
def self.dir_one_level options={}
|
|
965
|
+
|
|
966
|
+
Line.to_left
|
|
967
|
+
line = Line.value
|
|
968
|
+
indent = line[/^ */] + " " # Get indent
|
|
969
|
+
|
|
970
|
+
dir = Bookmarks.expand(Tree.construct_path)
|
|
971
|
+
|
|
972
|
+
remote = self.is_remote?(dir)
|
|
973
|
+
unless remote
|
|
974
|
+
# If C-2, just open in dired
|
|
975
|
+
if Keys.prefix == 2
|
|
976
|
+
# TODO: Open in 1st window
|
|
977
|
+
View.to_after_bar
|
|
978
|
+
$el.find_file dir
|
|
979
|
+
return
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
dirs, files = self.files_in_dir(dir, options) # Get dirs and files in it
|
|
984
|
+
|
|
985
|
+
if files.empty? && dirs.empty?
|
|
986
|
+
if ! File.exists? dir # If doesn't exist, show message
|
|
987
|
+
return Tree << "
|
|
988
|
+
> Directory '#{dir}' doesn't exist. Create it?
|
|
989
|
+
- @mkdir/
|
|
990
|
+
"
|
|
991
|
+
end
|
|
992
|
+
View.flash "- Directory is empty"#, :times=>2
|
|
993
|
+
Move.to_end
|
|
994
|
+
Notes.enter_junior
|
|
995
|
+
View << "- "
|
|
996
|
+
return
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
# Change path to proper indent
|
|
1000
|
+
dirs.collect!{|i| i.sub(/.*\/(.+)/, "#{indent}+ \\1/")}
|
|
1001
|
+
|
|
1002
|
+
# Change path to proper indent
|
|
1003
|
+
files.collect!{|i| i.sub(/.*\/(.+)/, "#{indent}+ \\1")}
|
|
1004
|
+
|
|
1005
|
+
Line.next
|
|
1006
|
+
left = $el.point
|
|
1007
|
+
|
|
1008
|
+
# Move .notes files to top
|
|
1009
|
+
files = files.select{|i| i =~ /\.notes$/} + files.select{|i| i !~ /\.notes$/}
|
|
1010
|
+
|
|
1011
|
+
both = options[:date_sort] || options[:size_sort] ?
|
|
1012
|
+
files + dirs : dirs + files
|
|
1013
|
+
View.insert(both.join("\n") + "\n")
|
|
1014
|
+
right = $el.point
|
|
1015
|
+
$el.goto_char left
|
|
1016
|
+
|
|
1017
|
+
Line.to_beginning
|
|
1018
|
+
|
|
1019
|
+
if options[:focus]
|
|
1020
|
+
Search.forward "^ +\\+ #{options[:focus]}$"
|
|
1021
|
+
Line.to_beginning
|
|
1022
|
+
Color.colorize :l
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
Tree.search(:left=>left, :right=>right, :always_search=>true)
|
|
1026
|
+
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
# def self.enter_snippet
|
|
1030
|
+
# start = selected_window
|
|
1031
|
+
# snippet = self.snippet
|
|
1032
|
+
# self.open_in_bar
|
|
1033
|
+
|
|
1034
|
+
# loc = Location.new
|
|
1035
|
+
# Line.to_left
|
|
1036
|
+
# #orig = $el.point
|
|
1037
|
+
|
|
1038
|
+
# # If at indented line
|
|
1039
|
+
# while(Line.matches(/^ /))
|
|
1040
|
+
# Line.previous
|
|
1041
|
+
# end
|
|
1042
|
+
|
|
1043
|
+
# # Check to see if it's the same file
|
|
1044
|
+
# if snippet[/.+\n.+\n/] == buffer_substring($el.point, Line.left(3))
|
|
1045
|
+
# Line.next 2
|
|
1046
|
+
# View.insert "#{snippet.sub(/.+\n.+\n/, '')}\n"
|
|
1047
|
+
# # Check to see if it's just in same dir
|
|
1048
|
+
# elsif snippet[/.+\n/] == buffer_substring(point, Line.left(2))
|
|
1049
|
+
# Line.next
|
|
1050
|
+
# View.insert "#{snippet.sub(/.+\n/, '')}\n"
|
|
1051
|
+
# else
|
|
1052
|
+
# View.insert "#{snippet}\n"
|
|
1053
|
+
# end
|
|
1054
|
+
|
|
1055
|
+
# loc.go
|
|
1056
|
+
# #goto_char orig
|
|
1057
|
+
|
|
1058
|
+
# select_window(start)
|
|
1059
|
+
# end
|
|
1060
|
+
|
|
1061
|
+
# Enter tree of selection at the spot (saved by AS).
|
|
1062
|
+
def self.enter_at_spot
|
|
1063
|
+
snippet = self.snippet
|
|
1064
|
+
Location.jump("0")
|
|
1065
|
+
View.insert snippet
|
|
1066
|
+
Location.save("0") # So any more will be entered after
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
# Enter what's in clipboard with | to on the left margin, with appropriate indent
|
|
1070
|
+
def self.enter_quote txt=nil
|
|
1071
|
+
|
|
1072
|
+
prefix = Keys.prefix :clear=>1
|
|
1073
|
+
|
|
1074
|
+
# Skip forward if on heading
|
|
1075
|
+
Line.to_left
|
|
1076
|
+
if Line[/^\|/]
|
|
1077
|
+
Move.to_end
|
|
1078
|
+
$el.newline
|
|
1079
|
+
end
|
|
1080
|
+
|
|
1081
|
+
txt ||= Clipboard.get(0, :add_linebreak=>1)
|
|
1082
|
+
|
|
1083
|
+
dir = Tree.construct_path rescue nil
|
|
1084
|
+
|
|
1085
|
+
if self.dir? && self.handles? # If current line is path
|
|
1086
|
+
Tree.plus_to_minus_maybe
|
|
1087
|
+
indent = Line.indent
|
|
1088
|
+
dir = Tree.construct_path
|
|
1089
|
+
t = Clipboard.get("=")
|
|
1090
|
+
t = t.gsub(/^#{dir}/, '')
|
|
1091
|
+
if t.sub!(/\A\n/, '') # If no dir left, indent one over
|
|
1092
|
+
t.gsub!(/^ /, '')
|
|
1093
|
+
# t = t.gsub(/^ /, '')
|
|
1094
|
+
end
|
|
1095
|
+
Tree.add_pluses_and_minuses t, '-', '-'
|
|
1096
|
+
Line.next
|
|
1097
|
+
View.insert "#{t}\n".gsub(/^/, "#{indent} ")
|
|
1098
|
+
return
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
# If empty line, just enter tree...
|
|
1102
|
+
|
|
1103
|
+
if Line.blank?
|
|
1104
|
+
|
|
1105
|
+
if prefix == :u || txt =~ /\A +[-+]?\|[-+ ]/ # If C-u or whole thing is quoted already, unquote
|
|
1106
|
+
txt = txt.grep(/\|/).join()
|
|
1107
|
+
return $el.insert(txt.gsub(/^ *[-+]?\|([-+ ]|$)/, "")) # Remove | ..., |+...., |<blank>, etc
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1110
|
+
start = $el.point
|
|
1111
|
+
txt = Clipboard.get("=")
|
|
1112
|
+
indent = prefix || 0 # Indent prefix spaces, or 2
|
|
1113
|
+
txt = txt.gsub(/^/, " " * indent)
|
|
1114
|
+
|
|
1115
|
+
View.insert txt
|
|
1116
|
+
$el.set_mark(Line.left(2))
|
|
1117
|
+
$el.goto_char start
|
|
1118
|
+
return
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
# Line has content, so indent under...
|
|
1122
|
+
|
|
1123
|
+
txt = txt.unindent # Unindent
|
|
1124
|
+
# TODO: don't unindent if up+?
|
|
1125
|
+
|
|
1126
|
+
indent = Line.indent # Get current indent
|
|
1127
|
+
on_quoted_line = Line.matches /^ +\|/
|
|
1128
|
+
Line.next
|
|
1129
|
+
|
|
1130
|
+
# Indent one level further unless on comment already
|
|
1131
|
+
unless on_quoted_line
|
|
1132
|
+
indent = "#{indent} "
|
|
1133
|
+
end
|
|
1134
|
+
|
|
1135
|
+
indent += " " * Keys.prefix_or_0 # If numeric prefix, add to indent
|
|
1136
|
+
txt = txt.sub /\n+\z/, '' # Remove last \n
|
|
1137
|
+
|
|
1138
|
+
# Quote unless already quoted
|
|
1139
|
+
quote = txt =~ /^\|/ ? '' : "| "
|
|
1140
|
+
|
|
1141
|
+
txt = txt.gsub /^/, "#{indent}#{quote}"
|
|
1142
|
+
txt = txt.gsub /^( *\|) $/, "\\1" # Remove blank lines
|
|
1143
|
+
|
|
1144
|
+
View.insert "#{txt}\n"
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
def self.enter_as_search
|
|
1148
|
+
indent = Line.indent
|
|
1149
|
+
Move.to_end
|
|
1150
|
+
View.insert "\n#{indent} - ###{Clipboard["0"]}/"
|
|
1151
|
+
Launcher.launch
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
# Remove the following lines indented more than the current one
|
|
1155
|
+
|
|
1156
|
+
# Expand if dir, or open if file
|
|
1157
|
+
def self.launch options={}
|
|
1158
|
+
|
|
1159
|
+
#Tree.plus_to_minus_maybe
|
|
1160
|
+
line = Line.value
|
|
1161
|
+
|
|
1162
|
+
indent = Line.indent
|
|
1163
|
+
list = nil
|
|
1164
|
+
path = Tree.construct_path # Get path
|
|
1165
|
+
|
|
1166
|
+
without_label = Line.without_label
|
|
1167
|
+
if Launcher.wrapper path # run files followed by slash as menus
|
|
1168
|
+
# Do nothing if it returned true
|
|
1169
|
+
elsif without_label =~ /^ *\$ / # $ shell command inline (sync)
|
|
1170
|
+
Console.launch :sync=>true
|
|
1171
|
+
elsif without_label =~ /^ *%( |$)/ # % shell command (async)
|
|
1172
|
+
Console.launch_async
|
|
1173
|
+
elsif without_label =~ /^ *&( |$)/ # % shell command in iterm
|
|
1174
|
+
Console.launch_async :iterm=>1
|
|
1175
|
+
elsif line =~ /^[^|\n]* (\*\*|##)/ # *foo or ## means do grep
|
|
1176
|
+
self.grep_syntax indent
|
|
1177
|
+
elsif self.dir? # if has slash - foo/ is a dir (if no | before)
|
|
1178
|
+
|
|
1179
|
+
# If is bookmark, delegate to wrapper / driller__
|
|
1180
|
+
# if without_label =~ /@?\$/
|
|
1181
|
+
|
|
1182
|
+
# # Use this instead
|
|
1183
|
+
|
|
1184
|
+
# Ol << "delegate to wrapper!"
|
|
1185
|
+
# return
|
|
1186
|
+
# end
|
|
1187
|
+
# Ol << "line: #{line.inspect}"
|
|
1188
|
+
|
|
1189
|
+
self.dir
|
|
1190
|
+
else
|
|
1191
|
+
self.open
|
|
1192
|
+
end
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1195
|
+
# Grabs matching lines in file and starts hide search
|
|
1196
|
+
def self.outline_pattern extension=nil, options={}
|
|
1197
|
+
extension ||= View.extension
|
|
1198
|
+
|
|
1199
|
+
if extension == "rb"
|
|
1200
|
+
"^\s*(def|class|module|it|describe|create_table|context) "
|
|
1201
|
+
elsif extension == "rake"
|
|
1202
|
+
"^\\s*(task|def|class) "
|
|
1203
|
+
elsif extension == "js"
|
|
1204
|
+
"(^ *(function)| = function\\()"
|
|
1205
|
+
elsif extension =~ /^notes|deck$/
|
|
1206
|
+
"^[\\|>]( |$)"
|
|
1207
|
+
else
|
|
1208
|
+
"^[^ \\t\\n]"
|
|
1209
|
+
end
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
#
|
|
1213
|
+
# Called by to+outline and enter+outline, (others?).
|
|
1214
|
+
#
|
|
1215
|
+
def self.enter_lines pattern=nil, options={}
|
|
1216
|
+
$xiki_no_search = false
|
|
1217
|
+
|
|
1218
|
+
# If prefix is 6, delegate to Git to enter methods by date
|
|
1219
|
+
if options[:prefix] == 6
|
|
1220
|
+
txt = Git.methods_by_date(Tree.path[-1]).join("\n")
|
|
1221
|
+
Tree.<< Tree.quote(txt), :no_slash=>1
|
|
1222
|
+
return
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
# If dir, delegate to C-. (they meant to just open it)
|
|
1226
|
+
return Launcher.launch if self.dir?
|
|
1227
|
+
|
|
1228
|
+
Tree.plus_to_minus
|
|
1229
|
+
|
|
1230
|
+
if Line.blank? # If blank line, get bookmark and enter into current file
|
|
1231
|
+
|
|
1232
|
+
bm = Keys.input(:timed => true, :prompt => "Enter bookmark to show outline for: ")
|
|
1233
|
+
path = Bookmarks.expand(bm, :just_bookmark => true)
|
|
1234
|
+
path = File.expand_path(path)
|
|
1235
|
+
#Files.directory? Bookmarks.expand("h", :just_bookmark=>true)
|
|
1236
|
+
|
|
1237
|
+
# If it's a dir, delegate to Open Tree
|
|
1238
|
+
if path =~ /\/$/
|
|
1239
|
+
self.ls :here=>true, :dir => path
|
|
1240
|
+
return
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
View.insert "- " + self.filename_to_next_line(path)
|
|
1244
|
+
$el.open_line 1
|
|
1245
|
+
end
|
|
1246
|
+
line = options[:path] || Line.value
|
|
1247
|
+
extension = line[/\.(\w+)$/, 1]
|
|
1248
|
+
if pattern.nil?
|
|
1249
|
+
return self.enter_lines(/#{self.outline_pattern extension}/, options)
|
|
1250
|
+
end
|
|
1251
|
+
Line.to_left
|
|
1252
|
+
path ||= options[:path] || Tree.construct_path # Get path
|
|
1253
|
+
path = Bookmarks.expand(path)
|
|
1254
|
+
indent = Line.indent # get indent
|
|
1255
|
+
|
|
1256
|
+
# If image, insert it
|
|
1257
|
+
return self.enter_image path if extension =~ /^(jpg|jpeg|png|gif)$/i
|
|
1258
|
+
|
|
1259
|
+
# Get matches from file
|
|
1260
|
+
matches = ""
|
|
1261
|
+
indent_more = options[:path] ? '' : ' '
|
|
1262
|
+
|
|
1263
|
+
if path =~ /^\/\w+@/
|
|
1264
|
+
contents = Remote.file_contents path
|
|
1265
|
+
Tree.under contents, :escape=>'| ', :no_slash=>1
|
|
1266
|
+
return
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
# Adjust so it finds if we're on the line
|
|
1270
|
+
current_line = options[:current_line] + 1 if options[:current_line]
|
|
1271
|
+
|
|
1272
|
+
line_found, matches_count, i = nil, 0, 0
|
|
1273
|
+
|
|
1274
|
+
if ! File.exists? path
|
|
1275
|
+
self.file_not_found_from_template(path)
|
|
1276
|
+
return
|
|
1277
|
+
end
|
|
1278
|
+
|
|
1279
|
+
IO.foreach(path) do |line|
|
|
1280
|
+
i+=1
|
|
1281
|
+
line.sub!(/[\r\n]+$/, '')
|
|
1282
|
+
|
|
1283
|
+
if current_line && line_found.nil?
|
|
1284
|
+
line_found = matches_count if i == current_line
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
next unless line =~ pattern
|
|
1288
|
+
line = line == "" ? "" : " #{line}"
|
|
1289
|
+
line.sub! /^ > $/, ' >'
|
|
1290
|
+
matches << "#{indent}#{indent_more}|#{line}\n"
|
|
1291
|
+
|
|
1292
|
+
matches_count+=1
|
|
1293
|
+
end
|
|
1294
|
+
|
|
1295
|
+
Tree.insert_quoted_and_search matches, :line_found=>line_found
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
def self.enter_image image
|
|
1299
|
+
tmp_dir = "/tmp/insert_image"
|
|
1300
|
+
Dir.mkdir tmp_dir if ! File.exists? tmp_dir
|
|
1301
|
+
|
|
1302
|
+
width, height = Console.sync("identify \"#{image}\"").match(/(\d+)x(\d+)/)[1..2]
|
|
1303
|
+
max = 300
|
|
1304
|
+
if width.to_i > max || height.to_i > max
|
|
1305
|
+
dest = tmp_dir+"/"+File.basename(image).sub(".", "_#{max}.")
|
|
1306
|
+
Console.sync %`convert "#{image}" -resize #{max}x#{max} "#{dest}"`, :dir=>"/tmp"
|
|
1307
|
+
else
|
|
1308
|
+
dest = image
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1311
|
+
Image.>> dest
|
|
1312
|
+
|
|
1313
|
+
Line.previous
|
|
1314
|
+
Line.to_beginning
|
|
1315
|
+
|
|
1316
|
+
nil
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
def self.file_not_found_from_template path
|
|
1321
|
+
|
|
1322
|
+
if path =~ /\/menus\/(.+)\.rb$/ # "
|
|
1323
|
+
name = $1
|
|
1324
|
+
View.flash "- File doesn't exist, start with this...", :times=>4
|
|
1325
|
+
Xiki.dont_search
|
|
1326
|
+
txt = File.read "#{Xiki.dir}/etc/templates/menu_template.rb"
|
|
1327
|
+
txt.gsub!(/\{\{(.+?)\}\}/) { eval $1 } # Expand {{these}}
|
|
1328
|
+
return Tree.<< txt, :escape=>"| ", :no_slash=>1
|
|
1329
|
+
|
|
1330
|
+
elsif path =~ /^#{File.expand_path("~/menus")}\/(.+)\.menu$/ # "
|
|
1331
|
+
View.flash "- File doesn't exist, start with this...", :times=>4
|
|
1332
|
+
Xiki.dont_search
|
|
1333
|
+
txt = File.read "#{Xiki.dir}/etc/templates/menu_template.menu"
|
|
1334
|
+
return Tree.<< txt, :escape=>"| ", :no_slash=>1
|
|
1335
|
+
end
|
|
1336
|
+
|
|
1337
|
+
name, extension = path.match(/([^\/]+)\.(.+)$/)[1..2] rescue [nil, nil]
|
|
1338
|
+
|
|
1339
|
+
[File.expand_path("~/xiki_stuff/templates/"), Bookmarks["$x/etc/templates"]].each do |dir|
|
|
1340
|
+
file = "#{dir}/template.#{extension}"
|
|
1341
|
+
next unless File.exists? file
|
|
1342
|
+
View.flash "- File doesn't exist, start with this...", :times=>4
|
|
1343
|
+
txt = File.read file
|
|
1344
|
+
txt.gsub!(/\{\{(.+?)\}\}/) { eval $1 } # Expand {{these}}
|
|
1345
|
+
return Tree.<< txt, :escape=>"| ", :no_slash=>1
|
|
1346
|
+
end
|
|
1347
|
+
|
|
1348
|
+
# If no templates matched
|
|
1349
|
+
View.flash "- File doesn't exist", :times=>1
|
|
1350
|
+
Tree.<< "|", :no_slash=>1
|
|
1351
|
+
Move.to_end
|
|
1352
|
+
View << ' '
|
|
1353
|
+
|
|
1354
|
+
end
|
|
1355
|
+
|
|
1356
|
+
# Mapped to shortcuts that displays the trees
|
|
1357
|
+
def self.tree options={}
|
|
1358
|
+
$xiki_no_search = false
|
|
1359
|
+
bm = Keys.input(:timed=>true, :prompt=>"Enter bookmark to show tree of: ")
|
|
1360
|
+
options.merge!(:focus=>View.file_name) if bm == "."
|
|
1361
|
+
dir = Keys.bookmark_as_path(:bm=>bm)
|
|
1362
|
+
|
|
1363
|
+
return if dir == :bookmark_doesnt_exist
|
|
1364
|
+
|
|
1365
|
+
dir = "/" if dir == :slash
|
|
1366
|
+
|
|
1367
|
+
dir = Bookmarks.dir_only(dir) if options[:recursive]
|
|
1368
|
+
options.merge!(:dir=>dir)
|
|
1369
|
+
|
|
1370
|
+
# If U prefix, open in bar
|
|
1371
|
+
if Keys.prefix_u?
|
|
1372
|
+
self.ls options.merge(:open_in_bar=>true)
|
|
1373
|
+
# Otherwise, open in new buffer
|
|
1374
|
+
else
|
|
1375
|
+
self.ls options
|
|
1376
|
+
end
|
|
1377
|
+
end
|
|
1378
|
+
|
|
1379
|
+
def self.parent
|
|
1380
|
+
return nil unless Line[/^ /]
|
|
1381
|
+
|
|
1382
|
+
orig = View.cursor # Store original location
|
|
1383
|
+
|
|
1384
|
+
Tree.to_parent
|
|
1385
|
+
parent = Line.without_label
|
|
1386
|
+
|
|
1387
|
+
View.cursor = orig
|
|
1388
|
+
parent
|
|
1389
|
+
end
|
|
1390
|
+
|
|
1391
|
+
# Returns a regexp to match the acronym
|
|
1392
|
+
def self.is_remote? path
|
|
1393
|
+
return path =~ /^\/\w+@/
|
|
1394
|
+
end
|
|
1395
|
+
|
|
1396
|
+
# Returns the files in the dir
|
|
1397
|
+
def self.files_in_dir dir, options={}
|
|
1398
|
+
if self.is_remote?(dir)
|
|
1399
|
+
self.remote_files_in_dir(dir)
|
|
1400
|
+
else
|
|
1401
|
+
|
|
1402
|
+
all = Dir.glob("#{dir}*", File::FNM_DOTMATCH).
|
|
1403
|
+
select {|i| i !~ /\/\.(\.*|svn|git)$/}. # Exclude some dirs (exclude entensions here too?)
|
|
1404
|
+
select {|i| i !~ /\/\.#/}.sort
|
|
1405
|
+
|
|
1406
|
+
if options[:date_sort]
|
|
1407
|
+
all = all.sort{|a,b| File.mtime(b) <=> File.mtime(a)}
|
|
1408
|
+
elsif options[:size_sort]
|
|
1409
|
+
all = all.sort{|a,b| File.size(b) <=> File.size(a)}
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
dirs = all.select{|i| FileTest.directory?(i)}#.sort
|
|
1413
|
+
files = all.select{|i| FileTest.file?(i)}#.sort
|
|
1414
|
+
[dirs, files]
|
|
1415
|
+
end
|
|
1416
|
+
end
|
|
1417
|
+
|
|
1418
|
+
def self.remote_file_contents file
|
|
1419
|
+
path, file = file.match(/(.+\/)(.+)/)[1..2]
|
|
1420
|
+
Remote.dir path, file # Delegate to Remote.dir
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
def self.remote_files_in_dir dir
|
|
1424
|
+
res = Remote.dir(dir)
|
|
1425
|
+
res.map!{|i| "#{dir}#{i}"}
|
|
1426
|
+
[res.select{|i| i =~ /\/$/}.map{|i| i.sub(/\/$/, '')}, res.select{|i| i !~ /\/$/}]
|
|
1427
|
+
end
|
|
1428
|
+
|
|
1429
|
+
def self.url_from_path verb, path
|
|
1430
|
+
server, path = path.match(/([\w.-]+)(\/.*)/)[1..2]
|
|
1431
|
+
"http://#{server}:7433/#{verb}#{path}"
|
|
1432
|
+
end
|
|
1433
|
+
|
|
1434
|
+
def self.indentify_path path
|
|
1435
|
+
result = ""
|
|
1436
|
+
indent = ""
|
|
1437
|
+
path.each do |i|
|
|
1438
|
+
result << "#{indent}#{i}\n"
|
|
1439
|
+
indent += " "
|
|
1440
|
+
end
|
|
1441
|
+
result
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
def self.tree_to_paths tree
|
|
1445
|
+
# TODO: implement
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
def self.filename_to_next_line path
|
|
1449
|
+
path.sub(/(.+)\//, "\\1/\n - ") # Add linebreak before filename
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1452
|
+
# If cursor on a tree, returns it, otherwise return path of current file
|
|
1453
|
+
def self.tree_path_or_this_file dir_only=false
|
|
1454
|
+
|
|
1455
|
+
# If in tree, use that dir
|
|
1456
|
+
path = self.handles? && Keys.prefix != :u ?
|
|
1457
|
+
Tree.construct_path :
|
|
1458
|
+
View.file
|
|
1459
|
+
|
|
1460
|
+
# path = Files.just_dir(path) if dir_only
|
|
1461
|
+
if dir_only
|
|
1462
|
+
path = path =~ /\/$/ ? path : File.dirname(path)+"/"
|
|
1463
|
+
end
|
|
1464
|
+
path
|
|
1465
|
+
end
|
|
1466
|
+
|
|
1467
|
+
def self.do_create_dir
|
|
1468
|
+
|
|
1469
|
+
path = self.tree_path_or_this_file :dir_only
|
|
1470
|
+
|
|
1471
|
+
`mkdir -p "#{path}"`
|
|
1472
|
+
View.flash "- Created: #{path}"
|
|
1473
|
+
end
|
|
1474
|
+
|
|
1475
|
+
# Indent txt to be one level lower than current line
|
|
1476
|
+
def self.one_view_in_bar_by_default= to
|
|
1477
|
+
@@one_view_in_bar_by_default = to
|
|
1478
|
+
end
|
|
1479
|
+
|
|
1480
|
+
def self.copy_path options={}
|
|
1481
|
+
Effects.blink :what=>:line
|
|
1482
|
+
|
|
1483
|
+
# Return dir of view's file if at left margin, U, or not ^[|@-]
|
|
1484
|
+
if Line !~ /^ +[|@+-]/ || Keys.prefix_u
|
|
1485
|
+
path = View.file
|
|
1486
|
+
else
|
|
1487
|
+
path = Xiki.trunk.last # Grab from wiki tree
|
|
1488
|
+
|
|
1489
|
+
path.sub! /\/$/, '' if Line.value !~ /\/$/ # If current line doesn't have trailing slash, remove it
|
|
1490
|
+
end
|
|
1491
|
+
|
|
1492
|
+
View.flash "- copied: #{path}", :times=>2
|
|
1493
|
+
return Clipboard["0"] = path
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1496
|
+
# Adds extra line if we're at the end of the file.
|
|
1497
|
+
# If there's no linebreak, it causes weird errors.
|
|
1498
|
+
def self.extra_line_if_end_of_file
|
|
1499
|
+
if Line.right == View.bottom
|
|
1500
|
+
Line.to_right
|
|
1501
|
+
$el.open_line(1)
|
|
1502
|
+
end
|
|
1503
|
+
end
|
|
1504
|
+
|
|
1505
|
+
def self.add_slash_maybe dir
|
|
1506
|
+
dir =~ /\/$/ ? dir : "#{dir}/"
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1509
|
+
def self.grep_syntax indent
|
|
1510
|
+
Tree.plus_to_minus_maybe
|
|
1511
|
+
dir = Tree.construct_path
|
|
1512
|
+
raw = Tree.construct_path(:raw => true, :list => true)
|
|
1513
|
+
|
|
1514
|
+
files = contents = nil
|
|
1515
|
+
if raw.join('') =~ /\*\*(.+)##(.+)/ # *foo means search in files
|
|
1516
|
+
files, contents = $1, $2.sub!(/\/$/, '')
|
|
1517
|
+
elsif raw.last =~ /\*\*(.+)/ # *foo means search in files
|
|
1518
|
+
files = $1
|
|
1519
|
+
elsif raw.last =~ /##(.+)/ # ##foo means search in filenames
|
|
1520
|
+
contents = $1.sub!(/\/$/, '')
|
|
1521
|
+
if raw[-2] =~ /\*\*(.+)/ # If prev is **, use it
|
|
1522
|
+
files = $1
|
|
1523
|
+
elsif raw[-2] =~ /(.+[^\/])$/ # If prev line is file, just show matches
|
|
1524
|
+
contents = Regexp.new(contents, Regexp::IGNORECASE)
|
|
1525
|
+
list = self.outline_search(Bookmarks.expand(dir), contents, " ")
|
|
1526
|
+
end
|
|
1527
|
+
end
|
|
1528
|
+
files.sub!(/\/$/, '') if files
|
|
1529
|
+
options = {:raw => true}
|
|
1530
|
+
options.merge!({:files => files}) if files
|
|
1531
|
+
|
|
1532
|
+
unless list # If not already gotten
|
|
1533
|
+
Search.append_log dir, "- ###{contents}/"
|
|
1534
|
+
list = self.grep dir, contents, options
|
|
1535
|
+
list.shift # Pull off first dir, so they'll be relative
|
|
1536
|
+
end
|
|
1537
|
+
Line.to_next
|
|
1538
|
+
left = $el.point
|
|
1539
|
+
tree = list.join("\n") + "\n"
|
|
1540
|
+
View.insert tree.gsub(/^/, indent)
|
|
1541
|
+
right = $el.point
|
|
1542
|
+
$el.goto_char left
|
|
1543
|
+
if Line.matches(/^\s*$/) # Do nothing
|
|
1544
|
+
elsif Line.matches(/^\s+\|/)
|
|
1545
|
+
if Search.outline_goto_once # If we want to go back to a line
|
|
1546
|
+
Line.next Search.outline_goto_once
|
|
1547
|
+
Search.outline_goto_once = nil
|
|
1548
|
+
end
|
|
1549
|
+
|
|
1550
|
+
Tree.search :left=>left, :right=>right
|
|
1551
|
+
elsif contents # If we searched for something (and it wasn't just the dir)
|
|
1552
|
+
Move.to_quote
|
|
1553
|
+
Tree.search :left=>left, :right=>right, :recursive_quotes=>true
|
|
1554
|
+
else
|
|
1555
|
+
Move.to_junior
|
|
1556
|
+
Tree.search :left=>left, :right=>right, :recursive=>true
|
|
1557
|
+
end
|
|
1558
|
+
end
|
|
1559
|
+
|
|
1560
|
+
def self.move_dir_to_junior
|
|
1561
|
+
Keys.prefix_times.times do
|
|
1562
|
+
orig = Location.new
|
|
1563
|
+
indent = Line.indent
|
|
1564
|
+
txt = View.paragraph(:start_here => true, :delete => true)
|
|
1565
|
+
View.insert self.move_dir_to_junior_internal(txt, Keys.prefix_u?)
|
|
1566
|
+
|
|
1567
|
+
orig.go
|
|
1568
|
+
|
|
1569
|
+
end
|
|
1570
|
+
end
|
|
1571
|
+
|
|
1572
|
+
def self.move_dir_to_junior_internal txt, prefix_u=nil
|
|
1573
|
+
|
|
1574
|
+
first, rest = txt.match(/(.+?\n)(.+)/m)[1..2]
|
|
1575
|
+
indent = first[/ */]
|
|
1576
|
+
|
|
1577
|
+
# Split off any text not under branch (of first line)
|
|
1578
|
+
# Text indented the same as the first line (and after)
|
|
1579
|
+
rest_and_siblings = rest.match(/(.*?)^(#{indent}[^ \n].*)/m)
|
|
1580
|
+
rest, siblings = rest_and_siblings ? rest_and_siblings[1..2] : [rest, ""]
|
|
1581
|
+
|
|
1582
|
+
# What does this do??
|
|
1583
|
+
if prefix_u
|
|
1584
|
+
rest.sub! /^\s*[+-] /, ''
|
|
1585
|
+
rest.gsub! /^ /, ''
|
|
1586
|
+
"#{first}#{rest}#{siblings}"
|
|
1587
|
+
else
|
|
1588
|
+
|
|
1589
|
+
first.sub! /(.+\/)(.+\/)$/, "\\1\n#{indent} - \\2"
|
|
1590
|
+
rest.gsub! /^/, " "
|
|
1591
|
+
"#{first}#{rest}#{siblings}"
|
|
1592
|
+
end
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
# Returns whether line is a dir (ends with "/")
|
|
1596
|
+
def self.dir? txt=nil
|
|
1597
|
+
txt ||= Line.value
|
|
1598
|
+
txt =~ /^[^,|\n]*\/$/
|
|
1599
|
+
end
|
|
1600
|
+
|
|
1601
|
+
def self.delete_file
|
|
1602
|
+
|
|
1603
|
+
# in_file_being_deleted = Line !~ /^[ +-]/ || Keys.prefix_u
|
|
1604
|
+
|
|
1605
|
+
# If not on tree, must want to delete this file
|
|
1606
|
+
# if in_file_being_deleted
|
|
1607
|
+
# dest_path = View.file
|
|
1608
|
+
# else
|
|
1609
|
+
dest_path = Tree.construct_path
|
|
1610
|
+
# end
|
|
1611
|
+
|
|
1612
|
+
dest_path = View.expand_path dest_path
|
|
1613
|
+
|
|
1614
|
+
return View.flash("- File doesn't exist: #{dest_path}", :times=>5) if ! File.exists?(dest_path)
|
|
1615
|
+
|
|
1616
|
+
View.flash "- Delete file for sure?"
|
|
1617
|
+
answer = Keys.input :chars=>1, :prompt=>"For sure delete?: #{dest_path}" #"
|
|
1618
|
+
|
|
1619
|
+
return unless answer =~ /y/i
|
|
1620
|
+
|
|
1621
|
+
executable = File.directory?(dest_path) ? "rm -r" : "rm"
|
|
1622
|
+
command = "#{executable} \"#{dest_path}\""
|
|
1623
|
+
|
|
1624
|
+
result = Console.run command, :sync=>true
|
|
1625
|
+
if (result||"").any?
|
|
1626
|
+
View.beep
|
|
1627
|
+
View.message "#{result}"
|
|
1628
|
+
return
|
|
1629
|
+
end
|
|
1630
|
+
|
|
1631
|
+
# return View.kill if in_file_being_deleted
|
|
1632
|
+
|
|
1633
|
+
Tree.kill_under
|
|
1634
|
+
Effects.glow :fade_out=>1
|
|
1635
|
+
Line.delete
|
|
1636
|
+
Line.to_beginning
|
|
1637
|
+
end
|
|
1638
|
+
|
|
1639
|
+
def self.move_latest_screenshot_to dest_path, dest_dir
|
|
1640
|
+
desktop = Bookmarks['$dt']
|
|
1641
|
+
|
|
1642
|
+
latest_screenshot = `ls -t #{desktop} "Screen shot*"`[/Screen shot .*/]
|
|
1643
|
+
|
|
1644
|
+
return View.message("No screenshot found.") if latest_screenshot.empty?
|
|
1645
|
+
|
|
1646
|
+
command = "mv \"#{desktop}#{latest_screenshot}\" \"#{dest_path}\""
|
|
1647
|
+
|
|
1648
|
+
result = Console.run command, :sync=>true
|
|
1649
|
+
View.message result
|
|
1650
|
+
end
|
|
1651
|
+
|
|
1652
|
+
def self.move_to
|
|
1653
|
+
self.copy_to :move=>1
|
|
1654
|
+
end
|
|
1655
|
+
|
|
1656
|
+
def self.copy_current_file_to options={}
|
|
1657
|
+
|
|
1658
|
+
return View.beep '- Save the file first!' if View.modified?
|
|
1659
|
+
|
|
1660
|
+
# Go from current file to bookmark
|
|
1661
|
+
source_path = View.file
|
|
1662
|
+
View.flash "- to which bookmark?"
|
|
1663
|
+
verb = options[:move] ? "Move" : "Copy"
|
|
1664
|
+
dest_path = Keys.bookmark_as_path :prompt=>"#{verb} current file to which bookmark? "
|
|
1665
|
+
return if ! dest_path.is_a? String
|
|
1666
|
+
# dest_path = File.dirname dest_path
|
|
1667
|
+
|
|
1668
|
+
dest_path = Bookmarks[dest_path]
|
|
1669
|
+
|
|
1670
|
+
executable = options[:move] ? "mv" : "cp -r"
|
|
1671
|
+
|
|
1672
|
+
command = "#{executable} \"#{source_path}\" \"#{dest_path}\""
|
|
1673
|
+
|
|
1674
|
+
result = Console.run command, :sync=>true
|
|
1675
|
+
|
|
1676
|
+
return View.beep result if (result||"").any? # If output isn't as expected, beep and show error
|
|
1677
|
+
|
|
1678
|
+
if options[:move]
|
|
1679
|
+
View.kill
|
|
1680
|
+
View.open "#{dest_path}#{File.basename source_path}"
|
|
1681
|
+
verb = "Moved"
|
|
1682
|
+
else
|
|
1683
|
+
verb = "Copied"
|
|
1684
|
+
end
|
|
1685
|
+
|
|
1686
|
+
View.flash "- #{verb} to: #{dest_path}"
|
|
1687
|
+
end
|
|
1688
|
+
|
|
1689
|
+
def self.copy_to options={}
|
|
1690
|
+
prefix = options[:prefix] || Keys.prefix
|
|
1691
|
+
Keys.clear
|
|
1692
|
+
|
|
1693
|
+
# Error if not in a file tree
|
|
1694
|
+
|
|
1695
|
+
return self.copy_current_file_to options if ! FileTree.handles?
|
|
1696
|
+
|
|
1697
|
+
dest_path = Tree.construct_path
|
|
1698
|
+
|
|
1699
|
+
dest_dir = dest_path.sub(/(.+\/).*/, "\\1")
|
|
1700
|
+
|
|
1701
|
+
if prefix == 2
|
|
1702
|
+
self.move_latest_screenshot_to dest_path, dest_dir
|
|
1703
|
+
return View.flash "- Moved the latest screenshot to here!"
|
|
1704
|
+
end
|
|
1705
|
+
|
|
1706
|
+
arg = options[:move] ? {:delete=>1} : {}
|
|
1707
|
+
source_path = Tree.dir_at_spot arg
|
|
1708
|
+
|
|
1709
|
+
stem = File.basename source_path # Cut of path of source
|
|
1710
|
+
indent = Line.indent
|
|
1711
|
+
|
|
1712
|
+
dest_is_dir = dest_path =~ /\/$/
|
|
1713
|
+
source_is_dir = source_path =~ /\/$/
|
|
1714
|
+
|
|
1715
|
+
if dest_is_dir # If dest is a dir, insert junior
|
|
1716
|
+
Line.next
|
|
1717
|
+
indent << " "
|
|
1718
|
+
dest_stem = stem
|
|
1719
|
+
|
|
1720
|
+
else # If file, use as dest
|
|
1721
|
+
dest_stem = Line.without_label
|
|
1722
|
+
end
|
|
1723
|
+
executable = options[:move] ? "mv" : "cp -r"
|
|
1724
|
+
|
|
1725
|
+
# Add ".1" if prefix is 1
|
|
1726
|
+
if prefix == 1
|
|
1727
|
+
dest_stem << '.1'
|
|
1728
|
+
end
|
|
1729
|
+
|
|
1730
|
+
command_dest_stem = dest_stem
|
|
1731
|
+
|
|
1732
|
+
if source_path =~ /\/$/ # If source is dir
|
|
1733
|
+
source_path.sub! /\/$/, '' # Remove slash, so it copies dir, not contents
|
|
1734
|
+
command_dest_stem = "" # put into, not replace
|
|
1735
|
+
end
|
|
1736
|
+
|
|
1737
|
+
command = "#{executable} \"#{source_path}\" \"#{dest_dir}#{command_dest_stem}\""
|
|
1738
|
+
|
|
1739
|
+
result = Console.run command, :sync=>true
|
|
1740
|
+
|
|
1741
|
+
if (result||"").any? # If output isn't as expected, beep and show error
|
|
1742
|
+
View.beep
|
|
1743
|
+
View.message "#{result}"
|
|
1744
|
+
return
|
|
1745
|
+
end
|
|
1746
|
+
|
|
1747
|
+
dest_stem << "/" if source_is_dir
|
|
1748
|
+
|
|
1749
|
+
Line.to_left
|
|
1750
|
+
if dest_is_dir || prefix == 1 # If it's a file, we didn't delete it
|
|
1751
|
+
Line.next if prefix == 1
|
|
1752
|
+
View.insert "#{indent}+ #{dest_stem}\n", :dont_move=>true
|
|
1753
|
+
Line.to_beginning
|
|
1754
|
+
Effects.glow :fade_in=>1
|
|
1755
|
+
Line.previous if prefix == 1
|
|
1756
|
+
end
|
|
1757
|
+
end
|
|
1758
|
+
|
|
1759
|
+
def self.rename_file
|
|
1760
|
+
# If dired mode, use wdired
|
|
1761
|
+
return $el.wdired_change_to_wdired_mode if $el.elvar.major_mode.to_s == "dired-mode"
|
|
1762
|
+
|
|
1763
|
+
column = View.column
|
|
1764
|
+
|
|
1765
|
+
if ! Line[/^ *[+-] /] # Error if not indented and ^/- /
|
|
1766
|
+
View.beep
|
|
1767
|
+
return View.message "TODO: implement renaming current file?"
|
|
1768
|
+
end
|
|
1769
|
+
source_path = Tree.construct_path
|
|
1770
|
+
is_dir = source_path =~ /\/$/
|
|
1771
|
+
source_path.sub! /\/$/, ''
|
|
1772
|
+
|
|
1773
|
+
new_name = Keys.input(
|
|
1774
|
+
:prompt=>"Rename #{source_path} to what?: ",
|
|
1775
|
+
:initial_input=>(Keys.prefix_u? ? File.basename(source_path) : "")
|
|
1776
|
+
)
|
|
1777
|
+
|
|
1778
|
+
dest_path = "#{source_path.sub(/(.+\/).+/, "\\1#{new_name}")}"
|
|
1779
|
+
|
|
1780
|
+
command = "mv \"#{source_path}\" \"#{dest_path}\""
|
|
1781
|
+
|
|
1782
|
+
Console.run command, :sync=>true
|
|
1783
|
+
|
|
1784
|
+
indent = Line.indent
|
|
1785
|
+
Effects.glow :fade_out=>1
|
|
1786
|
+
Line.delete
|
|
1787
|
+
View.insert((is_dir ? "#{indent}- #{new_name}/\n" : "#{indent}+ #{new_name}\n"), :dont_move=>true)
|
|
1788
|
+
|
|
1789
|
+
View.column = column
|
|
1790
|
+
Effects.glow :fade_in=>1
|
|
1791
|
+
end
|
|
1792
|
+
|
|
1793
|
+
def self.open_as_upper where=false
|
|
1794
|
+
orig_u = Keys.prefix_u
|
|
1795
|
+
view = View.current
|
|
1796
|
+
path = Tree.construct_path(:list=>true)
|
|
1797
|
+
|
|
1798
|
+
if where == :lowest
|
|
1799
|
+
Move.to_window 9
|
|
1800
|
+
else
|
|
1801
|
+
View.to_upper
|
|
1802
|
+
end
|
|
1803
|
+
was_at_top = View.start == $el.point
|
|
1804
|
+
$el.split_window_vertically
|
|
1805
|
+
|
|
1806
|
+
View.next if where # :lowest or :second
|
|
1807
|
+
|
|
1808
|
+
if was_at_top
|
|
1809
|
+
where ? View.previous : View.next
|
|
1810
|
+
$el.recenter 0
|
|
1811
|
+
where ? View.next : View.previous
|
|
1812
|
+
end
|
|
1813
|
+
|
|
1814
|
+
self.open :path=>path, :same_view=>true
|
|
1815
|
+
View.to_window(view) if orig_u
|
|
1816
|
+
end
|
|
1817
|
+
|
|
1818
|
+
def self.to_outline
|
|
1819
|
+
prefix = Keys.prefix :clear=>true
|
|
1820
|
+
args = [View.txt, View.line] if prefix == :u || prefix == :-
|
|
1821
|
+
|
|
1822
|
+
current_line = Line.number
|
|
1823
|
+
|
|
1824
|
+
path = View.file
|
|
1825
|
+
View.to_buffer("*tree outline")
|
|
1826
|
+
View.clear; Notes.mode
|
|
1827
|
+
if path
|
|
1828
|
+
dir, file = path.match(/(.+\/)(.+)/)[1..2]
|
|
1829
|
+
txt = "- #{dir}\n - #{file}\n"
|
|
1830
|
+
View.insert txt
|
|
1831
|
+
View.to_top
|
|
1832
|
+
self.select_next_file
|
|
1833
|
+
else
|
|
1834
|
+
View.insert "- buffer/\n"
|
|
1835
|
+
View.to_top
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1838
|
+
case prefix
|
|
1839
|
+
when 2 # Prompt to add @... menu under file
|
|
1840
|
+
Move.to_end
|
|
1841
|
+
Xiki.insert_menu
|
|
1842
|
+
when 4 # Get ready to run $... shell command on file
|
|
1843
|
+
$el.delete_char(1)
|
|
1844
|
+
View << "$ "
|
|
1845
|
+
ControlLock.disable
|
|
1846
|
+
when 6 # List methods by date (use 'git blame')
|
|
1847
|
+
self.enter_lines nil, :prefix=>6
|
|
1848
|
+
when 7
|
|
1849
|
+
Move.to_end
|
|
1850
|
+
View << "\n @info"
|
|
1851
|
+
Launcher.launch
|
|
1852
|
+
when 8
|
|
1853
|
+
Launcher.enter_all
|
|
1854
|
+
when 0
|
|
1855
|
+
# Just show path if 0+...
|
|
1856
|
+
when :- # Just show # foo... comments...
|
|
1857
|
+
self.enter_lines(/(^ *def |(^| )# .+\.\.\.$)/, :current_line=>current_line)
|
|
1858
|
+
when 6 # Just show # foo... comments...
|
|
1859
|
+
self.enter_lines(/(^ *def |self\.)/, :current_line=>current_line)
|
|
1860
|
+
when :u
|
|
1861
|
+
txt, line = Search.deep_outline *args
|
|
1862
|
+
Tree.<< txt, :line_found=>line, :escape=>'| ', :no_slash=>1
|
|
1863
|
+
else
|
|
1864
|
+
self.enter_lines nil, :current_line=>current_line
|
|
1865
|
+
end
|
|
1866
|
+
|
|
1867
|
+
end
|
|
1868
|
+
|
|
1869
|
+
def self.skip_dirs dir, skip_these
|
|
1870
|
+
dir = Bookmarks[dir].sub /\/$/, ''
|
|
1871
|
+
(@skip ||= {})[dir] = skip_these
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
end
|
|
1875
|
+
FileTree.define_styles
|