redcar 0.10 → 0.11.0dev

Sign up to get free protection for your applications and to get access to all the features.
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