
    G@dZA                         d Z ddlZddlZddlZddlmZ ddlZddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZ ddlmZ  ej        e          Z G d d	e          Z G d
 de          ZdS )a  
Autosave components for the Editor plugin and the EditorStack widget

The autosave system regularly checks the contents of all opened files and saves
a copy in the autosave directory if the contents are different from the
autosave file (if it exists) or original file (if there is no autosave file).

The mapping between original files and autosave files is stored in the
variable `name_mapping` and saved in the file `pidNNN.txt` in the autosave
directory, where `NNN` stands for the pid. This filename is chosen so that
multiple instances of Spyder can run simultaneously.

File contents are compared using their hash. The variable `file_hashes`
contains the hash of all files currently open in the editor and all autosave
files.

On startup, the contents of the autosave directory is checked and if autosave
files are found, the user is asked whether to recover them;
see `spyder/plugins/editor/widgets/recover.py`.
    N)QTimer)_get_conf_pathrunning_under_pytest)AutosaveErrorDialog)RecoveryDialog)is_spyder_processc                       e Zd ZdZdZd Zed             Zej        d             Zed             Z	e	j        d             Z	d Z
d	 Zd
 Zd Zd Zd ZdS )AutosaveForPluginag  
    Component of editor plugin implementing autosave functionality.

    Attributes:
        name_mapping (dict): map between names of opened and autosave files.
        file_hashes (dict): map between file names and hash of their contents.
            This is used for both files opened in the editor and their
            corresponding autosave files.
    i`  c                    || _         i | _        i | _        t          | j                   | _        | j                            d           | j        j                            | j                   d| _	        | j
        | _        dS )z
        Constructor.

        Autosave is disabled after construction and needs to be enabled
        explicitly if required.

        Args:
            editor (Editor): editor plugin.
        TFN)editorname_mappingfile_hashesr   timersetSingleShottimeoutconnectdo_autosave_enabledDEFAULT_AUTOSAVE_INTERVAL	_interval)selfr   s     Dlib/python3.11/site-packages/spyder/plugins/editor/utils/autosave.py__init__zAutosaveForPlugin.__init__>   ss     DK((

  &&&
""4#34447    c                     | j         S )z
        Get or set whether autosave component is enabled.

        The setter will start or stop the autosave component if appropriate.
        )r   r   s    r   enabledzAutosaveForPlugin.enabledQ   s     }r   c                 ~    || j         k    rd S |                                  || _        |                                  d S N)r   stop_autosave_timerr   start_autosave_timer)r   new_enableds     r   r   zAutosaveForPlugin.enabledZ   sE    $,&&F  """#!!#####r   c                     | j         S )z
        Interval between two autosaves, in milliseconds.

        The setter will perform an autosave if the interval is changed and
        autosave is enabled.
        )r   r   s    r   intervalzAutosaveForPlugin.intervalb   s     ~r   c                     || j         k    rd S |                                  || _        | j        r|                                  d S d S r    )r%   r!   r   r   r   )r   new_intervals     r   r%   zAutosaveForPlugin.intervall   sY    4=((F  """%< 		 	r   c                 V    | j         r!| j                            | j                   dS dS )z
        Start a timer which calls do_autosave() after `self.interval`.

        The autosave timer is only started if autosave is enabled.
        N)r   r   startr%   r   s    r   r"   z&AutosaveForPlugin.start_autosave_timeru   s6     < 	,JT]+++++	, 	,r   c                 8    | j                                          dS )zStop the autosave timer.N)r   stopr   s    r   r!   z%AutosaveForPlugin.stop_autosave_timer~   s    
r   c                     t                               d           | j                                        }|j                                         |                                  dS )z?Instruct current editorstack to autosave files where necessary.zAutosave triggeredN)loggerdebugr   get_current_editorstackautosaveautosave_allr"   )r   stacks     r   r   zAutosaveForPlugin.do_autosave   sT    )***3355##%%%!!#####r   c           
      t   t          d          }t          j        |t          j                  sg g fS g }g }g }g }t          j        |          D ]}t          j        ||          }t          j        d|          }|r|	                    |           t                              d                    |                     t          |          5 }	|	                                }
	 t          j        |
          }n# t"          t$          f$ rl t                              d                    |                     t                              d                    t)          |
                               i }Y nw xY w|d |                                D             z  }ddd           n# 1 swxY w Y   t-          |                    d                    }t1          |          r/t                              d	                    |                     |t3          |                                          z  }|	                    |           t5          |          t5          |          z
  D ]F}|	                    d|f           t                              d
                    |                     G||fS )aI  
        Get list of files to recover from pid files in autosave dir.

        This returns a tuple `(files_to_recover, pid_files)`. In this tuple,
        `files_to_recover` is a list of tuples containing the original file
        names and the corresponding autosave file names, as recorded in the
        pid files in the autosave directory. Any files in the autosave
        directory which are not listed in a pid file, are also included, with
        the original file name set to `None`. The second entry, `pid_files`,
        is a list with the names of the pid files.
        r0   zpid([0-9]*)\.txt\ZzReading pid file: {}zError parsing pid file {}zContents: {}c                     g | ]\  }}|S  r5   ).0origr0   s      r   
<listcomp>z:AutosaveForPlugin.get_files_to_recover.<locals>.<listcomp>   s/     (@ (@ (@5EdH (@ (@ (@r   N   zIgnoring files in {}zAdded unmentioned file: {})r   osaccessR_OKlistdirospjoinrematchappendr-   r.   formatopenreadastliteral_evalSyntaxError
ValueErrorerrorrepritemsintgroupr	   listset)r   autosave_dirfiles_to_recoverfiles_mentioned	pid_filesnon_pid_filesname	full_namerA   pidfiletxttxt_as_dictpidfilenames                 r   get_files_to_recoverz&AutosaveForPlugin.get_files_to_recover   s    %Z00yrw// 	r6M	 J|,, 	0 	0Dt44IH2D99E 0  +++3::9EEFFF)__ @!,,..C)&)&6s&;&;'4 ) ) )%@&,fY&7&79 9 9^%:%:499%E%EFFF&() $ (@ (@+6+<+<+>+>(@ (@ (@ @O@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ %++a..))$S)) BLL!7!>!>y!I!IJJJJ$[->->-@-@(A(AA$$$$Y//// M**S-A-AA 	H 	HH##T8$4555LL5<<XFFGGGG**s7   F(&C;:F(;A:E85F(7E88$F((F,	/F,	c                 J   |                                  \  }}t                      r| j        n| j        j        }t	          ||          }|                                 |j        dd         | _        |D ].}	 t          j	        |           # t          t          f$ r Y +w xY wdS )z
        Offer to recover files from autosave.

        Read pid files to get a list of files that can possibly be recovered,
        then ask the user what to do with these files, and finally remove
        the pid files.
        )parentN)r]   r   r   mainr   exec_if_nonemptyfiles_to_openrecover_files_to_openr:   removeIOErrorOSError)r   rR   pidfilesr_   dialogrX   s         r   try_recover_from_autosavez+AutosaveForPlugin.try_recover_from_autosave   s     &*%>%>%@%@"( 4 6 6LDK<L 0@@@!!!%+%9!!!%<" 	 	G	'""""W%   	 	s   7BB B c                 6    | j         |_         | j        |_        dS )a"  
        Register an AutosaveForStack object.

        This replaces the `name_mapping` and `file_hashes` attributes
        in `autosave_for_stack` with references to the corresponding
        attributes of `self`, so that all AutosaveForStack objects
        share the same data.
        N)r   r   )r   autosave_for_stacks     r   register_autosave_for_stackz-AutosaveForPlugin.register_autosave_for_stack   s"     +/*;')-)9&&&r   N)__name__
__module____qualname____doc__r   r   propertyr   setterr%   r"   r!   r   r]   ri   rl   r5   r   r   r   r   0   s          !*8 8 8&   X ^$ $ ^$   X _  _, , ,  $ $ $6+ 6+ 6+p  &
: 
: 
: 
: 
:r   r   c                   H    e Zd ZdZd Zd Zd Zd Zd Zd Z	d Z
d	 Zd
 ZdS )AutosaveForStacka0  
    Component of EditorStack implementing autosave functionality.

    In Spyder, the `name_mapping` and `file_hashes` are set to references to
    the corresponding variables in `AutosaveForPlugin`.

    Attributes:
        stack (EditorStack): editor stack this component belongs to.
        name_mapping (dict): map between names of opened and autosave files.
        file_hashes (dict): map between file names and hash of their contents.
            This is used for both files opened in the editor and their
            corresponding autosave files.
    c                 0    || _         i | _        i | _        dS )z|
        Constructor.

        Args:
            editorstack (EditorStack): editor stack this component belongs to.
        N)r2   r   r   )r   editorstacks     r   r   zAutosaveForStack.__init__   s!     !
r   c                    t          j        |          }t          j        ||          }|| j                                        v st          j        |          rd}t          j        |          \  }}|| j                                        v st          j        |          r`|dz  }d                    |||          }t          j        ||          }|| j                                        v Lt          j        |          `|S )aC  
        Create unique autosave file name for specified file name.

        The created autosave file name does not yet exist either in
        `self.name_mapping` or on disk.

        Args:
            filename (str): original file name
            autosave_dir (str): directory in which autosave files are stored
        r   r9   z{}-{}{})r>   basenamer?   r   valuesexistssplitextrC   )	r   r\   rQ   rx   autosave_filenamecounterrootextautosave_basenames	            r   create_unique_autosave_filenamez0AutosaveForStack.create_unique_autosave_filename   s    <))H\8<<!2!9!9!;!;;;:/00 <GX..ID#$(9(@(@(B(BBBj!233 C1$-$4$4T7C$H$H!$'H\;L$M$M!	 %(9(@(@(B(BBBj!233 C
 ! r   c                    t          d          }t          j                    }t          j        |d                    |                    }| j        rRt          |d          5 }|                    t          | j                             ddd           dS # 1 swxY w Y   dS 	 t          j
        |           dS # t          t          f$ r Y dS w xY w)a/  
        Writes current autosave mapping to a pidNNN.txt file.

        This function should be called after updating `self.autosave_mapping`.
        The NNN in the file name is the pid of the Spyder process. If the
        current autosave mapping is empty, then delete the file if it exists.
        r0   z	pid{}.txtwN)r   r:   getpidr>   r?   rC   r   rD   writeasciird   re   rf   )r   rQ   my_pidpidfile_namerX   s        r   save_autosave_mappingz&AutosaveForStack.save_autosave_mapping  s    %Z00xk.@.@.H.HII 	lC(( 8GeD$5667778 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8	,'''''W%   s$   "(BBB%B; ;CCc                    || j         vrdS | j         |         }	 t          j        |           n]# t          $ rP}t	          d                              |          }t          ||          }|                                 Y d}~nd}~ww xY w| j         |= 	 | j        |= n# t          $ r Y nw xY w| 
                                 t                              d|           dS )z
        Remove autosave file for specified file.

        This function also updates `self.name_mapping` and `self.file_hashes`.
        If there is no autosave file, then the function returns without doing
        anything.
        Nz%Error while removing autosave file {}zRemoving autosave file %s)r   r:   rd   EnvironmentErrorr   rC   r   exec_if_enabledr   KeyErrorr   r-   r.   )r   r\   r|   rJ   actionmsgboxs         r   remove_autosave_filez%AutosaveForStack.remove_autosave_file'  s    4,,,F -h7	%I'(((( 	% 	% 	%?@@v/00 (77F""$$$$$$$$		%
 h'
	 !233 	 	 	D	 	""$$$02CDDDDDs'   / 
B	ABB	B 
B+*B+c                    	 | j         |         }n# t          $ r t          d          }t          j        |          s`	 t          j        |           nJ# t          $ r=}t          d          }t          ||          }|
                                 Y d}~nd}~ww xY w|                     ||          }|| j         |<   |                                  t                              d           Y nw xY w|S )ab  
        Get name of autosave file for specified file name.

        This function uses the dict in `self.name_mapping`. If `filename` is
        in the mapping, then return the corresponding autosave file name.
        Otherwise, construct a unique file name and update the mapping.

        Args:
            filename (str): original file name
        r0   z'Error while creating autosave directoryNzNew autosave file name)r   r   r   r>   isdirr:   mkdirr   r   r   r   r   r   r-   r.   )r   r\   r|   rQ   rJ   r   r   s          r   get_autosave_filenamez&AutosaveForStack.get_autosave_filenameF  s$   	3 $ 1( ; 	3 	3 	3(44L9\** --H\****' - - -HIIF0??F**,,,,,,,,- !% D Dl!, !,*;Dh'&&(((LL122222	3 ! s9    -C.AC.
B3BC.BAC.-C.c                    | j         j        |         }|j        rdS |j        }	 | j        |         }n-# t
          $ r  t                              d|           d}Y nw xY w| j                             |          }|| j	        v rV| j	        |         }| j        |         }||k    r4||k    r| 
                    |           dS |                     |           dS dS ||k    r|                     |           dS dS )a!  
        Autosave a file if necessary.

        If the file is newly created (and thus not named by the user), do
        nothing.  If the current contents are the same as the autosave file
        (if it exists) or the original file (if no autosave filee exists),
        then do nothing. If the current contents are the same as the file on
        disc, but the autosave file is different, then remove the autosave
        file. In all other cases, autosave the file.

        Args:
            index (int): index into self.stack.data
        Nz#KeyError when retrieving hash of %s)r2   datanewly_createdr\   r   r   r-   r.   compute_hashr   r   r0   )r   indexfinfoorig_filename	orig_hashnew_hashr|   autosave_hashs           r   maybe_autosavezAutosaveForStack.maybe_autosavec  s6    
& 	F	(7II 	 	 	
 LL>NNNIII	 :**511D--- $ 1- @ ,->?M=((y((--m<<<<<MM%(((((	 )( 9$$e$$$$$ %$s   2 'AAc                    |                      |j                  }t                              d|j        |           	 | j                            ||           | j                            |          }|| j        |<   dS # t          $ rW}t          d          
                    |j        |          }t          ||          }|                                 Y d}~dS d}~ww xY w)a=  
        Autosave a file.

        Save a copy in a file with name `self.get_autosave_filename()` and
        update the cached hash of the autosave file. An error dialog notifies
        the user of any errors raised when saving.

        Args:
            fileinfo (FileInfo): file that is to be autosaved.
        zAutosaving %s to %szError while autosaving {} to {}N)r   r\   r-   r.   r2   _write_to_filer   r   r   r   rC   r   r   )r   r   r|   r   rJ   r   r   s          r   r0   zAutosaveForStack.autosave  s     !66u~FF*EN<MNNN	%J%%e->??? J33E::M2?D./// 	% 	% 	%9::ven.?@@ (77F""$$$$$$$$$		%s   ?A> >
CACCc                     t          | j                                                  D ]}|                     |           dS )z*Autosave all opened files where necessary.N)ranger2   get_stack_countr   )r   r   s     r   r1   zAutosaveForStack.autosave_all  sH    4:557788 	' 	'E&&&&	' 	'r   c                 0   	 | j         |         }n.# t          $ r! t                              d||           d}Y nw xY w|                     |           || j         |= || j         |<   | j                            |          }|                     |           dS )z
        Update autosave files after a file is renamed.

        Args:
            old_name (str): name of file before it is renamed
            new_name (str): name of file after it is renamed
        z&KeyError when handling rename %s -> %sN)r   r   r-   rJ   r   r2   has_filenamer   )r   old_namenew_nameold_hashr   s        r   file_renamedzAutosaveForStack.file_renamed  s    	'1HH 	 	 	LLA!8- - -HHH		
 	!!(+++ *)1DX&
''11E"""""s    (;;N)rm   rn   ro   rp   r   r   r   r   r   r   r0   r1   r   r5   r   r   rt   rt      s         	 	 	! ! !0  (E E E>! ! !:(% (% (%T% % %.' ' '
# # # # #r   rt   )rp   rF   loggingr:   os.pathpathr>   r@   qtpy.QtCorer   spyder.config.baser   r   r   +spyder.plugins.editor.widgets.autosaveerrorr   %spyder.plugins.editor.widgets.recoverr   spyder.utils.programsr	   	getLoggerrm   r-   objectr   rt   r5   r   r   <module>r      s7   , 


  				       				       F E E E E E E E E E K K K K K K @ @ @ @ @ @ 3 3 3 3 3 3 
	8	$	$n: n: n: n: n: n: n: n:b\# \# \# \# \#v \# \# \# \# \#r   