<s> import re <EOL> from collections import namedtuple , defaultdict <EOL> import sublime <EOL> from sublime_plugin import WindowCommand , TextCommand <EOL> from . . git_command import GitCommand <EOL> from ... common import util <EOL> BlamedLine = namedtuple ( "<STR_LIT>" , ( "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" ) ) <EOL> NOT_COMMITED_HASH = "<STR_LIT>" <EOL> BLAME_TITLE = "<STR_LIT>" <EOL> class GsBlameCommand ( WindowCommand , GitCommand ) : <EOL> @ util . view . single_cursor_coords <EOL> def run ( self , coords , file_path = None , repo_path = None ) : <EOL> self . _coords = coords <EOL> self . __file_path = file_path or self . file_path <EOL> self . __repo_path = repo_path or self . repo_path <EOL> sublime . set_timeout_async ( self . run_async ) <EOL> def run_async ( self ) : <EOL> self . window . show_quick_panel ( <EOL> [ <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> "<STR_LIT>" , <EOL> ] , <EOL> self . on_option_selection , <EOL> selected_index = self . quick_panel_blame_idx <EOL> ) <EOL> def on_option_selection ( self , index ) : <EOL> if index == - <NUM_LIT:1> : <EOL> return <EOL> self . quick_panel_blame_idx = index <EOL> ignore_whitespace = "<STR_LIT>" if index > <NUM_LIT:0> else None <EOL> detect_move_or_copy = [ None , None , "<STR_LIT>" , "<STR_LIT>" , "<STR_LIT>" ] [ index ] <EOL> view = self . window . new_file ( ) <EOL> view . set_syntax_file ( "<STR_LIT>" ) <EOL> view . settings ( ) . set ( "<STR_LIT>" , True ) <EOL> view . settings ( ) . set ( "<STR_LIT>" , self . __repo_path ) <EOL> view . settings ( ) . set ( "<STR_LIT>" , self . __file_path ) <EOL> view . settings ( ) . set ( "<STR_LIT>" , False ) <EOL> view . settings ( ) . set ( "<STR_LIT>" , False ) <EOL> view . settings ( ) . set ( '<STR_LIT>' , [ ] ) <EOL> view . set_name ( BLAME_TITLE . format ( self . get_rel_path ( self . __file_path ) ) ) <EOL> view . set_scratch ( True ) <EOL> view . run_command ( "<STR_LIT>" , { <EOL> "<STR_LIT>" : self . _coords , <EOL> "<STR_LIT>" : ignore_whitespace , <EOL> "<STR_LIT>" : detect_move_or_copy <EOL> } ) <EOL> class GsBlameInitializeViewCommand ( TextCommand , GitCommand ) : <EOL> def run ( self , edit , coords = None , ignore_whitespace = None , detect_move_or_copy = None ) : <EOL> content = self . get_content ( <EOL> ignore_whitespace = ignore_whitespace , <EOL> detect_move_or_copy = detect_move_or_copy <EOL> ) <EOL> self . view . sel ( ) . clear ( ) <EOL> self . view . set_read_only ( False ) <EOL> self . view . replace ( edit , sublime . Region ( <NUM_LIT:0> , self . view . size ( ) ) , content ) <EOL> self . view . set_read_only ( True ) <EOL> if coords is not None : <EOL> self . scroll_to ( coords ) <EOL> def get_content ( self , ignore_whitespace = None , detect_move_or_copy = None ) : <EOL> blame_porcelain = self . git ( <EOL> "<STR_LIT>" , "<STR_LIT>" , ignore_whitespace , detect_move_or_copy , self . file_path <EOL> ) <EOL> blamed_lines , commits = self . parse_blame ( blame_porcelain . splitlines ( ) ) <EOL> commit_infos = { <EOL> commit_hash : self . short_commit_info ( commit ) <EOL> for commit_hash , commit in commits . items ( ) <EOL> } <EOL> partitions = tuple ( self . partition ( blamed_lines ) ) <EOL> longest_commit_line = max ( <EOL> ( line <EOL> for commit_info in commit_infos . values ( ) <EOL> for line in commit_info ) , <EOL> key = len ) <EOL> longest_code_line = max ( <EOL> ( line . contents for partition in partitions for line in partition ) , <EOL> key = len <EOL> ) <EOL> partitions_with_commits_iter = self . couple_partitions_and_commits ( <EOL> partitions = partitions , <EOL> commit_infos = commit_infos , <EOL> left_pad = len ( longest_commit_line ) <EOL> ) <EOL> spacer = ( <EOL> "<STR_LIT:->" * len ( longest_commit_line ) + <EOL> "<STR_LIT>" + <EOL> "<STR_LIT:->" * ( <NUM_LIT:5> + len ( longest_code_line ) ) + <EOL> "<STR_LIT:\n>" <EOL> ) <EOL> return spacer . join ( partitions_with_commits_iter ) <EOL> def parse_blame ( self , blame_porcelain ) : <EOL> lines_iter = iter ( blame_porcelain ) <EOL> blamed_lines = [ ] <EOL> commits = defaultdict ( lambda : defaultdict ( str ) ) <EOL> for line in lines_iter : <EOL> commit_hash , orig_lineno , final_lineno , _ = re . match ( r"<STR_LIT>" , line ) . groups ( ) <EOL> commits [ commit_hash ] [ "<STR_LIT>" ] = commit_hash [ : <NUM_LIT:12> ] <EOL> commits [ commit_hash ] [ "<STR_LIT>" ] = commit_hash <EOL> next_line = next ( lines_iter ) <EOL> while not next_line . startswith ( "<STR_LIT:\t>" ) : <EOL> try : <EOL> k , v = re . match ( r"<STR_LIT>" , next_line ) . groups ( ) <EOL> except AttributeError as e : <EOL> print ( "<STR_LIT>" + repr ( next_line ) ) <EOL> commits [ commit_hash ] [ k ] = v <EOL> next_line = next ( lines_iter ) <EOL> blamed_lines . append ( BlamedLine ( <EOL> contents = next_line [ <NUM_LIT:1> : ] , <EOL> commit_hash = commit_hash , <EOL> orig_lineno = orig_lineno , <EOL> final_lineno = final_lineno ) ) <EOL> return blamed_lines , commits <EOL> @ staticmethod <EOL> def partition ( blamed_lines ) : <EOL> prev_line = None <EOL> current_hunk = [ ] <EOL> for line in blamed_lines : <EOL> if prev_line and line . commit_hash != prev_line . commit_hash : <EOL> yield current_hunk <EOL> current_hunk = [ ] <EOL> prev_line = line <EOL> current_hunk . append ( line ) <EOL> yield current_hunk <EOL> @ staticmethod <EOL> def short_commit_info ( commit ) : <EOL> if commit [ "<STR_LIT>" ] == NOT_COMMITED_HASH : <EOL> return ( "<STR_LIT>" , ) <EOL> summary = commit [ "<STR_LIT>" ] <EOL> if len ( summary ) > <NUM_LIT> : <EOL> summary = summary [ : <NUM_LIT> ] + "<STR_LIT>" <EOL> author_info = commit [ "<STR_LIT>" ] + "<STR_LIT:U+0020>" + commit [ "<STR_LIT>" ] <EOL> time_stamp = util . dates . fuzzy ( commit [ "<STR_LIT>" ] ) if commit [ "<STR_LIT>" ] else "<STR_LIT>" <EOL> return ( summary , commit [ "<STR_LIT>" ] , author_info , time_stamp ) <EOL> @ staticmethod <EOL> def couple_partitions_and_commits ( partitions , commit_infos , left_pad ) : <EOL> left_fallback = "<STR_LIT:U+0020>" * left_pad <EOL> right_fallback = "<STR_LIT>" <EOL> for partition in partitions : <EOL> output = "<STR_LIT>" <EOL> commit_info = commit_infos [ partition [ <NUM_LIT:0> ] . commit_hash ] <EOL> left_len = len ( commit_info ) <EOL> right_len = len ( partition ) <EOL> total_lines = len ( max ( ( commit_info , partition ) , key = len ) ) <EOL> for i in range ( total_lines ) : <EOL> left = commit_info [ i ] if i < left_len else left_fallback <EOL> right = partition [ i ] . contents if i < right_len else right_fallback <EOL> lineno = partition [ i ] . final_lineno if i < right_len else right_fallback <EOL> output += "<STR_LIT>" . format ( left = left , left_pad = left_pad ) <EOL> if len ( right ) : <EOL> output += "<STR_LIT>" . format ( lineno = lineno , right = right ) <EOL> output += "<STR_LIT:\n>" <EOL> yield output <EOL> def scroll_to ( self , coords ) : <EOL> pattern = r"<STR_LIT>" . format ( lineno = coords [ <NUM_LIT:0> ] + <NUM_LIT:1> ) <EOL> corresponding_region = self . view . find ( pattern , <NUM_LIT:0> ) <EOL> blame_view_pt = corresponding_region . b <EOL> self . view . sel ( ) . add ( sublime . Region ( blame_view_pt , blame_view_pt ) ) <EOL> sublime . set_timeout_async ( lambda : self . view . show_at_center ( blame_view_pt ) , <NUM_LIT:0> ) <EOL> class GsBlameOpenCommitCommand ( TextCommand ) : <EOL> @ util . view . single_cursor_pt <EOL> def run ( self , cursor_pt , edit ) : <EOL> hunk_start = util . view . get_instance_before_pt ( self . view , cursor_pt , r"<STR_LIT>" ) <EOL> if hunk_start is None : <EOL> short_hash_row = <NUM_LIT:1> <EOL> else : <EOL> hunk_start_row , _ = self . view . rowcol ( hunk_start ) <EOL> short_hash_row = hunk_start_row + <NUM_LIT:2> <EOL> short_hash_pos = self . view . text_point ( short_hash_row , <NUM_LIT:0> ) <EOL> short_hash = self . view . substr ( sublime . Region ( short_hash_pos , short_hash_pos + <NUM_LIT:12> ) ) <EOL> if not short_hash . strip ( ) : <EOL> return <EOL> self . view . window ( ) . run_command ( "<STR_LIT>" , { "<STR_LIT>" : short_hash } ) </s>
