redcar 0.10 → 0.11.0dev

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.
Files changed (77) hide show
  1. data/CHANGES +14 -0
  2. data/Rakefile +68 -29
  3. data/lib/redcar.rb +2 -2
  4. data/plugins/application/lib/application/dialogs/filter_list_dialog.rb +2 -1
  5. data/plugins/application_swt/spec/application_swt/gradient_spec.rb +3 -12
  6. data/plugins/core/lib/core/resource.rb +13 -5
  7. data/plugins/document_search/features/find.feature +366 -214
  8. data/plugins/document_search/features/incremental_search.feature +351 -0
  9. data/plugins/document_search/features/replace.feature +16 -16
  10. data/plugins/document_search/features/step_definitions/find_steps.rb +16 -0
  11. data/plugins/document_search/features/support/env.rb +11 -0
  12. data/plugins/document_search/lib/document_search.rb +149 -109
  13. data/plugins/document_search/lib/document_search/commands.rb +251 -202
  14. data/plugins/document_search/lib/document_search/find_speedbar.rb +138 -81
  15. data/plugins/document_search/lib/document_search/incremental_search_speedbar.rb +70 -0
  16. data/plugins/document_search/lib/document_search/query_options.rb +15 -39
  17. data/plugins/document_search/plugin.rb +1 -1
  18. data/plugins/edit_view/features/step_definitions/editing_steps.rb +6 -2
  19. data/plugins/edit_view_swt/lib/edit_view_swt.rb +2 -2
  20. data/plugins/file_parser/lib/file_parser.rb +6 -1
  21. data/plugins/html_view/features/step_definitions/web_view_steps.rb +12 -0
  22. data/plugins/html_view/features/support/env.rb +16 -0
  23. data/plugins/html_view/lib/html_view.rb +5 -1
  24. data/plugins/line_tools/lib/line_tools.rb +16 -0
  25. data/plugins/project/features/find_file.feature +28 -0
  26. data/plugins/project/features/open_and_save_files.feature +11 -0
  27. data/plugins/project/features/step_definitions/file_steps.rb +6 -1
  28. data/plugins/project/features/support/env.rb +2 -0
  29. data/plugins/project/lib/project.rb +49 -6
  30. data/plugins/project/lib/project/commands.rb +18 -6
  31. data/plugins/project/lib/project/find_file_dialog.rb +19 -8
  32. data/plugins/project/lib/project/find_recent_dialog.rb +30 -0
  33. data/plugins/project/lib/project/manager.rb +41 -10
  34. data/plugins/project/lib/project/recent.rb +64 -0
  35. data/plugins/project/spec/fixtures/myproject/vendor/bar.rb +0 -0
  36. data/plugins/project/spec/fixtures/myproject/vendor/plugins/bar.rb +0 -0
  37. data/plugins/{find-in-project → project_search}/TODO.md +3 -3
  38. data/plugins/project_search/features/support/env.rb +6 -0
  39. data/plugins/project_search/features/word_search.feature +34 -0
  40. data/plugins/project_search/lib/project_search.rb +73 -0
  41. data/plugins/project_search/lib/project_search/binary_data_detector.rb +46 -0
  42. data/plugins/project_search/lib/project_search/commands.rb +62 -0
  43. data/plugins/project_search/lib/project_search/hit.rb +17 -0
  44. data/plugins/{find-in-project/lib/find_in_project → project_search/lib/project_search}/images/collapsed.png +0 -0
  45. data/plugins/{find-in-project/lib/find_in_project → project_search/lib/project_search}/images/expanded.png +0 -0
  46. data/plugins/{find-in-project/lib/find_in_project → project_search/lib/project_search}/images/spinner.gif +0 -0
  47. data/plugins/project_search/lib/project_search/lucene_index.rb +64 -0
  48. data/plugins/project_search/lib/project_search/lucene_refresh.rb +22 -0
  49. data/plugins/project_search/lib/project_search/project.rb +14 -0
  50. data/plugins/project_search/lib/project_search/query.rb +29 -0
  51. data/plugins/{find-in-project/lib/find_in_project → project_search/lib/project_search}/stylesheets/style.css +14 -3
  52. data/plugins/project_search/lib/project_search/views/_file.html.erb +60 -0
  53. data/plugins/{find-in-project/lib/find_in_project → project_search/lib/project_search}/views/index.html.erb +12 -9
  54. data/plugins/project_search/lib/project_search/word_search.rb +105 -0
  55. data/plugins/project_search/lib/project_search/word_search_controller.rb +207 -0
  56. data/plugins/project_search/plugin.rb +8 -0
  57. data/plugins/project_search/spec/fixtures/project/binary_file.bin +0 -0
  58. data/plugins/project_search/spec/fixtures/project/foo.txt +43 -0
  59. data/plugins/project_search/spec/fixtures/project/qux.rb +3 -0
  60. data/plugins/project_search/spec/project_search/binary_data_detector_spec.rb +24 -0
  61. data/plugins/project_search/spec/project_search/word_search_spec.rb +157 -0
  62. data/plugins/project_search/spec/spec_helper.rb +27 -0
  63. data/plugins/redcar/redcar.rb +77 -71
  64. data/plugins/ruby/lib/ruby/syntax_checker.rb +1 -1
  65. data/plugins/snippets/features/snippets.feature +12 -0
  66. data/plugins/snippets/lib/snippets/tab_handler.rb +1 -1
  67. metadata +46 -25
  68. data/plugins/document_search/features/find_and_replace.feature +0 -723
  69. data/plugins/document_search/lib/document_search/find_and_replace_speedbar.rb +0 -142
  70. data/plugins/find-in-project/lib/find_in_project.rb +0 -35
  71. data/plugins/find-in-project/lib/find_in_project/commands.rb +0 -30
  72. data/plugins/find-in-project/lib/find_in_project/controllers.rb +0 -170
  73. data/plugins/find-in-project/lib/find_in_project/views/_divider.html.erb +0 -4
  74. data/plugins/find-in-project/lib/find_in_project/views/_file_heading.html.erb +0 -10
  75. data/plugins/find-in-project/lib/find_in_project/views/_file_line.html.erb +0 -6
  76. data/plugins/find-in-project/plugin.rb +0 -11
  77. data/plugins/project/lib/project/recent_directories.rb +0 -54
@@ -0,0 +1,64 @@
1
+ module Redcar
2
+ class Project
3
+ # Purpose of this class is to have a menu that shows the 10 most recent opened files and directories
4
+ # This way users can quickly go to files and directories they have recently opened, or use frequently
5
+ class Recent
6
+
7
+ # Create menus for recent files and directories
8
+ def self.storage
9
+ @storage ||= begin
10
+ storage = Plugin::Storage.new('recent')
11
+ storage.set_default('list', [])
12
+ storage.set_default('max_list_length', 100)
13
+ storage.set_default('max_recent_menu_length', 10)
14
+ storage
15
+ end
16
+ end
17
+
18
+ def self.generate_menu(builder)
19
+ recent = storage['list']
20
+ count = 0
21
+ recent.each do |path|
22
+ if count >= storage['max_recent_menu_length']
23
+ break
24
+ end
25
+ count = count.next
26
+
27
+ if File.exist?(File.expand_path(path))
28
+ if File.directory?(path)
29
+ builder.item(File.basename(path) + "/") do
30
+ Project::Manager.open_project_for_path(path)
31
+ end
32
+ elsif File.file?(File.expand_path(path))
33
+ builder.item(File.basename(path)) do
34
+ Project::Manager.open_file(path)
35
+ end
36
+ else
37
+ remove_path(path)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ # Stores the given path to the text file, to appear in the recents menu.
45
+ #
46
+ # @param [String] path the path of a file or directory to be saved
47
+ def self.store_path(path)
48
+ path = File.expand_path(path)
49
+ storage["list"].delete(path)
50
+ storage["list"].unshift(path)
51
+ if storage["list"].length == storage['max_list_length'] + 1
52
+ storage["list"].pop
53
+ end
54
+ storage.save
55
+ end
56
+
57
+ def self.remove_path(path)
58
+ path = File.expand_path(path)
59
+ storage["list"].delete(path)
60
+ storage.save
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,11 +1,11 @@
1
- * Speed it up! (in particular, with context). Stats below.
1
+ * Speed up regex search (in particular, with context). Stats below.
2
2
 
3
3
  Searching for "redcar" in the Redcar source code:
4
4
  - Without Context:
5
5
  - Textmate AckMate: ~2 seconds
6
- - Redcar find-in-project: ~50 seconds
6
+ - Redcar regex search: ~50 seconds
7
7
  - With Context:
8
8
  - Textmate AckMate: ~4 seconds
9
- - Redcar find-in-project: ~5 minutes
9
+ - Redcar regex search: ~5 minutes
10
10
 
11
11
  * Per-project settings
@@ -0,0 +1,6 @@
1
+
2
+ Before do
3
+ end
4
+
5
+ After do
6
+ end
@@ -0,0 +1,34 @@
1
+ Feature: Word search
2
+ As a user
3
+ I want to search for the occurrences of complete words in my project
4
+
5
+ Background:
6
+ Given I will choose "plugins/project_search/spec/fixtures/project" from the "open_directory" dialog
7
+ And I open a directory
8
+ When I open the "Word Search" from the "Project" menu
9
+ And I wait "3" seconds
10
+
11
+ Scenario: See the search interface
12
+ Then I should see "Search for complete words only" in the web view
13
+
14
+ Scenario: Run a search
15
+ When I fill in "#query" with "foo" in the web view
16
+ And I press "Find In Project" in the web view
17
+ Then I should see "foo bar baz" in the web view
18
+
19
+ #Scenario: Opening the TODO list shows the todo items and their action texts without colons
20
+ # Then the HTML tab should say "a course of action"
21
+ # And the HTML tab should not say ": a course of action"
22
+ #
23
+ #Scenario: Opening the TODO list shows the todo item's file names
24
+ # Then the HTML tab should say "OPTIMIZE_colon.file"
25
+ # And the HTML tab should say "FIXME.file"
26
+ #
27
+ #Scenario: Opening the TODO list shows the todo item's line numbers
28
+ # Then the HTML tab should say "OPTIMIZE_colon.file:1"
29
+ # And the HTML tab should say "FIXME.file:3"
30
+ #
31
+ #Scenario: Clicking a TODO item's file name should open it in an edit tab
32
+ # When I click "FIXME.file" in the HTML tab
33
+ # Then my active tab should be "FIXME.file"
34
+ # And the contents should be "#\n#\n<c># FIXME note"
@@ -0,0 +1,73 @@
1
+
2
+ $:.push(File.dirname(__FILE__) + "/../vendor/lucene/lib")
3
+ require 'lucene'
4
+
5
+ require 'project_search/word_search_controller'
6
+ require 'project_search/lucene_index'
7
+ require 'project_search/lucene_refresh'
8
+ require 'project_search/binary_data_detector'
9
+ require 'project_search/commands'
10
+ require 'project_search/hit'
11
+ require 'project_search/word_search'
12
+
13
+ class ProjectSearch
14
+ def self.menus
15
+ Redcar::Menu::Builder.build do
16
+ sub_menu "Project" do
17
+ group :priority => 1 do
18
+ item "Search", :command => ProjectSearch::WordSearchCommand
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.keymaps
25
+ osx = Redcar::Keymap.build("main", :osx) do
26
+ link "Cmd+Shift+F", ProjectSearch::WordSearchCommand
27
+ end
28
+ linwin = Redcar::Keymap.build("main", [:linux, :windows]) do
29
+ link "Ctrl+Shift+F", ProjectSearch::WordSearchCommand
30
+ end
31
+ [osx, linwin]
32
+ end
33
+
34
+ def self.toolbars
35
+ Redcar::ToolBar::Builder.build do
36
+ item "Search", :command => WordSearchCommand,
37
+ :icon => File.join(Redcar::ICONS_DIRECTORY, "application-search-result.png"),
38
+ :barname => :project
39
+ end
40
+ end
41
+
42
+ def self.storage
43
+ @storage ||= begin
44
+ storage = Redcar::Plugin::Storage.new('find_in_project')
45
+ storage.set_default('recent_queries', [])
46
+ storage.set_default('excluded_dirs', ['.git', '.svn', '.redcar'])
47
+ storage.set_default('excluded_files', [])
48
+ storage.set_default('excluded_patterns', [/tags$/, /\.log$/])
49
+ storage.set_default('literal_match', false)
50
+ storage.set_default('match_case', false)
51
+ storage.set_default('with_context', false)
52
+ storage.set_default('context_lines', 2)
53
+ storage.save
54
+ end
55
+ end
56
+
57
+ Lucene::Config.use do |config|
58
+ config[:store_on_file] = true
59
+ config[:storage_path] = ""
60
+ config[:id_field] = :id
61
+ end
62
+
63
+
64
+ def self.project_refresh_task_type
65
+ LuceneRefresh
66
+ end
67
+
68
+ def self.indexes
69
+ @indexes ||= {}
70
+ end
71
+ end
72
+
73
+
@@ -0,0 +1,46 @@
1
+
2
+ class ProjectSearch
3
+ # This class discriminates between binary and textual data.
4
+ class BinaryDataDetector
5
+ # Is the data binary?
6
+ #
7
+ # @param [String] data
8
+ def self.binary?(data)
9
+ not textual?(data)
10
+ end
11
+
12
+ # Is the data plain text?
13
+ #
14
+ # @param [String] data
15
+ def self.textual?(data)
16
+ found_good_byte = false
17
+ data.each_byte do |b|
18
+ return false if bad_byte?(b)
19
+ found_good_byte = found_good_byte || good_byte?(b)
20
+ end
21
+ found_good_byte
22
+ end
23
+
24
+ INDIVIDUAL_GOOD_BYTES = [9, 10, 13]
25
+
26
+ # At least one of these good bytes must be found for the text to be
27
+ # discriminated as plain text.
28
+ #
29
+ # @param [Fixnum] byte
30
+ def self.good_byte?(byte)
31
+ (byte >= 32 and byte <= 255) or INDIVIDUAL_GOOD_BYTES.include?(byte)
32
+ end
33
+
34
+ INDIVIDUAL_BAD_BYTES = [0, 6, 14]
35
+
36
+ # If any of these bytes are found then we immediately discriminate the data
37
+ # as binary.
38
+ #
39
+ # @param [Fixnum] byte
40
+ def self.bad_byte?(byte)
41
+ (byte >= 14 and byte <= 31) or INDIVIDUAL_BAD_BYTES.include?(byte)
42
+ end
43
+ end
44
+ end
45
+
46
+
@@ -0,0 +1,62 @@
1
+
2
+ class ProjectSearch
3
+ class WordSearchCommand < Redcar::Command
4
+ sensitize :open_project
5
+
6
+ def find_open_instance
7
+ all_tabs = Redcar.app.focussed_window.notebooks.map { |nb| nb.tabs }.flatten
8
+ all_tabs.find do |t|
9
+ t.is_a?(Redcar::HtmlTab) && t.title == ProjectSearch::WordSearchController::TITLE
10
+ end
11
+ end
12
+
13
+ def execute
14
+ if project = Redcar::Project::Manager.focussed_project
15
+ if (tab = find_open_instance)
16
+ tab.html_view.refresh
17
+ tab.focus
18
+ else
19
+ index = ProjectSearch.indexes[project.path]
20
+ if index and index.has_content?
21
+ tab = win.new_tab(Redcar::HtmlTab)
22
+ tab.html_view.controller = ProjectSearch::WordSearchController.new
23
+ tab.focus
24
+ else
25
+ Redcar::Application::Dialog.message_box("Your project is still being indexed.", :type => :error)
26
+ end
27
+ end
28
+ else
29
+ Redcar::Application::Dialog.message_box("You need an open project to be able to use Find In Project!", :type => :error)
30
+ end
31
+ return
32
+ end
33
+ end
34
+
35
+ class RegexSearchCommand < Redcar::Command
36
+ sensitize :open_project
37
+
38
+ def execute
39
+ if Redcar::Project::Manager.focussed_project
40
+ if (tab = find_open_instance)
41
+ tab.html_view.controller = tab.html_view.controller # refresh
42
+ else
43
+ tab = win.new_tab(Redcar::HtmlTab)
44
+ tab.html_view.controller = ProjectSearch::RegexSearchController.new
45
+ end
46
+ tab.focus
47
+ else
48
+ # warning
49
+ Redcar::Application::Dialog.message_box("You need an open project to be able to use Find In Project!", :type => :error)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def find_open_instance
56
+ all_tabs = Redcar.app.focussed_window.notebooks.map { |nb| nb.tabs }.flatten
57
+ all_tabs.find do |t|
58
+ t.is_a?(Redcar::HtmlTab) && t.title == ProjectSearch::RegexSearchController.new.title
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+
2
+ class ProjectSearch
3
+ class Hit
4
+ attr_reader :file, :line_num, :pre_context, :post_context
5
+
6
+ def initialize(file, line_num, line, regex,
7
+ pre_context, post_context)
8
+ @file, @line_num, @line, @regex = file, line_num, line, regex
9
+ @pre_context = pre_context
10
+ @post_context = post_context
11
+ end
12
+
13
+ def line(start_with=nil, end_with=nil)
14
+ @line.gsub(@regex) { start_with.to_s + $& + end_with.to_s }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,64 @@
1
+
2
+ class ProjectSearch
3
+ class LuceneIndex
4
+ attr_accessor :last_updated, :lucene_index
5
+
6
+ def initialize(project)
7
+ @project = project
8
+ @has_content = false
9
+ load
10
+ end
11
+
12
+ def timestamp_file_path
13
+ File.join(@project.config_dir, 'lucene_last_updated')
14
+ end
15
+
16
+ def load
17
+ @last_updated = Time.at(0)
18
+ if File.exist?(timestamp_file_path)
19
+ @last_updated = Time.at(File.read(timestamp_file_path).chomp.to_i)
20
+ @has_content = true
21
+ end
22
+ end
23
+
24
+ def dump
25
+ File.open(timestamp_file_path, "w") do |fout|
26
+ fout.puts(last_updated.to_i.to_s)
27
+ end
28
+ end
29
+
30
+ def has_content?
31
+ @has_content
32
+ end
33
+
34
+ def update
35
+ changed_files = @project.file_list.changed_since(last_updated)
36
+ @last_updated = Time.now
37
+ changed_files.reject! do |fn, ts|
38
+ fn.index(@project.config_dir)
39
+ end
40
+ Lucene::Transaction.run do
41
+ @lucene_index ||= Lucene::Index.new(File.join(@project.config_dir, "lucene"))
42
+ begin
43
+ @lucene_index.field_infos[:contents][:store] = true
44
+ @lucene_index.field_infos[:contents][:tokenized] = true
45
  changed_files.each do |fn, ts|
46
+ unless File.basename(fn)[0..0] == "." or fn.include?(".git")
47
+ contents = File.read(fn)
48
+ unless BinaryDataDetector.binary?(contents[0..200])
49
+ adjusted_contents = contents.gsub(/\.([^\s])/, '. \1')
50
+ @lucene_index << { :id => fn, :contents => adjusted_contents }
51
+ end
52
+ end
53
+ end
54
+ @lucene_index.commit
55
+ @has_content = true
56
+ rescue => e
57
+ puts e.message
58
+ puts e.backtrace
59
+ end
60
+ end
61
+ dump
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,22 @@
1
+
2
+ class ProjectSearch
3
+
4
+ class LuceneRefresh < Redcar::Task
5
+ def initialize(project)
6
+ @project = project
7
+ end
8
+
9
+ def description
10
+ "#{@project.path}: refresh index"
11
+ end
12
+
13
+ def execute
14
+ return if @project.remote?
15
+ unless index = ProjectSearch.indexes[@project.path]
16
+ index = ProjectSearch::LuceneIndex.new(@project)
17
+ ProjectSearch.indexes[@project.path] = index
18
+ end
19
+ index.update
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+
2
+ module Redcar
3
+ class Project
4
+ def search(query, options)
5
+ options ||= {}
6
+
7
+ strategy = (options[:regex] || false) ? RegexSearch : WordSearch
8
+ match_case = options[:match_case] || true
9
+ context = options[:context] || 0
10
+
11
+ strategy.new(ProjectSearch.indexes[self], query, match_case, context)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+
2
+ class ProjectSearch
3
+ class Query
4
+ attr_reader :query_string
5
+
6
+ # Construct a
7
+ #
8
+ #
9
+ def initialize(project, query_string, regex, match_case, context)
10
+ @project = project
11
+ @query_string = query_string
12
+ @regex = regex
13
+ @match_case = match_case
14
+ @context = context
15
+ end
16
+
17
+ def match_case?
18
+ @match_case
19
+ end
20
+
21
+ def context
22
+ @context
23
+ end
24
+
25
+ def regex?
26
+ @regex
27
+ end
28
+ end
29
+ end