Skip to content

Commit

Permalink
Speed
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed Oct 30, 2023
1 parent e328ecb commit 1e22d49
Show file tree
Hide file tree
Showing 32 changed files with 671 additions and 896 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ gemspec
group :rubocop do
gem "rubocop-shopify", require: false
end

gem "prism", github: "ruby/prism"
9 changes: 8 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
GIT
remote: https://github.com/ruby/prism
revision: 4ff8fe2f0f72ebdb52706b4898ccf20083fc92a3
specs:
prism (0.15.1)

PATH
remote: .
specs:
smart_todo (1.6.0)
rexml
prism

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -52,6 +58,7 @@ PLATFORMS
DEPENDENCIES
bundler (>= 1.17)
minitest (~> 5.0)
prism!
rake (>= 10.0)
rubocop-shopify
smart_todo!
Expand Down
16 changes: 16 additions & 0 deletions bin/profile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "smart_todo"

class NullDispatcher < SmartTodo::Dispatchers::Base
class << self
def validate_options!(_); end
end
def dispatch
end
end
exit SmartTodo::CLI.new(NullDispatcher).run
17 changes: 3 additions & 14 deletions lib/smart_todo.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
# frozen_string_literal: true

require "prism"
require "smart_todo/version"
require "smart_todo/events"

module SmartTodo
autoload :SlackClient, "smart_todo/slack_client"
autoload :CLI, "smart_todo/cli"

module Parser
autoload :CommentParser, "smart_todo/parser/comment_parser"
autoload :TodoNode, "smart_todo/parser/todo_node"
autoload :MetadataParser, "smart_todo/parser/metadata_parser"
end

module Events
autoload :Date, "smart_todo/events/date"
autoload :GemBump, "smart_todo/events/gem_bump"
autoload :GemRelease, "smart_todo/events/gem_release"
autoload :IssueClose, "smart_todo/events/issue_close"
autoload :RubyVersion, "smart_todo/events/ruby_version"
end
autoload :Todo, "smart_todo/todo"
autoload :CommentParser, "smart_todo/comment_parser"

module Dispatchers
autoload :Base, "smart_todo/dispatchers/base"
Expand Down
58 changes: 46 additions & 12 deletions lib/smart_todo/cli.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# frozen_string_literal: true

require "optionparser"
require "etc"

module SmartTodo
# This class is the entrypoint of the SmartTodo library and is responsible
# to retrieve the command line options as well as iterating over each files/directories
# to run the +CommentParser+ on.
class CLI
def initialize
def initialize(dispatcher = nil)
@options = {}
@errors = []
@dispatcher = dispatcher
end

# @param args [Array<String>]
Expand All @@ -19,15 +21,18 @@ def run(args = ARGV)

paths << "." if paths.empty?

comment_parser = CommentParser.new
paths.each do |path|
normalize_path(path).each do |file|
parse_file(file)
normalize_path(path).each do |filepath|
comment_parser.parse_file(filepath)

$stdout.print(".")
$stdout.flush
end
end

process_dispatches(process_todos(comment_parser.todos))

if @errors.empty?
0
else
Expand Down Expand Up @@ -79,25 +84,54 @@ def normalize_path(path)
end
end

# @param file [String] a path to a file
def parse_file(file)
Parser::CommentParser.new(File.read(file, encoding: "UTF-8")).parse.each do |todo_node|
def process_todos(todos)
events = Events.new
dispatches = []

todos.each do |todo|
event_message = nil
event_met = todo_node.metadata.events.find do |event|
event_message = Events.public_send(event.method_name, *event.arguments)
event_met = todo.events.find do |event|
event_message = events.public_send(event.method_name, *event.arguments)
rescue => e
message = "Error while parsing #{file} on event `#{event.method_name}` with arguments #{event.arguments}: " \
message = "Error while parsing #{todo.filepath} on event `#{event.method_name}` " \
"with arguments #{event.arguments.map(&:inspect)}: " \
"#{e.message}"

@errors << message

nil
end

@errors.concat(todo_node.metadata.errors)

dispatcher.new(event_message, todo_node, file, @options).dispatch if event_met
@errors.concat(todo.errors)
dispatches << [event_message, todo] if event_met
end

dispatches
end

def process_dispatches(dispatches)
queue = Queue.new
dispatches.each { |dispatch| queue << dispatch }

thread_count = Etc.nprocessors
thread_count.times { queue << nil }

threads =
thread_count.times.map do
Thread.new do
Thread.current.abort_on_exception = true

loop do
dispatch = queue.pop
break if dispatch.nil?

(event_message, todo) = dispatch
dispatcher.new(event_message, todo, todo.filepath, @options).dispatch
end
end
end

threads.each(&:join)
end
end
end
49 changes: 49 additions & 0 deletions lib/smart_todo/comment_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module SmartTodo
class CommentParser
attr_reader :todos

def initialize
@todos = []
end

def parse(source, filepath = "-e")
parse_comments(Prism.parse_inline_comments(source, filepath), filepath)
end

def parse_file(filepath)
parse_comments(Prism.parse_file_inline_comments(filepath), filepath)
end

class << self
def parse(source)
parser = new
parser.parse(source)
parser.todos
end
end

private

def parse_comments(comments, filepath)
current_todo = nil

comments.each do |comment|
source = comment.location.slice

if source.match?(/^#\sTODO\(/)
todos << current_todo if current_todo
current_todo = Todo.new(source, filepath)
elsif current_todo && (indent = source[/^#(\s*)/, 1].length) && (indent - current_todo.indent == 2)
current_todo << "#{source[(indent + 1)..]}\n"
else
todos << current_todo if current_todo
current_todo = nil
end
end

todos << current_todo if current_todo
end
end
end
2 changes: 1 addition & 1 deletion lib/smart_todo/dispatchers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def initialize(event_message, todo_node, file, options)
@todo_node = todo_node
@options = options
@file = file
@assignees = @todo_node.metadata.assignees
@assignees = @todo_node.assignees
end

# This method gets called when a TODO reminder is expired and needs to be delivered.
Expand Down
Loading

0 comments on commit 1e22d49

Please sign in to comment.