* 'master' of https://github.com/prati0100/git-gui: git-gui: create a new namespace for chord script evaluation git-gui: reduce Tcl version requirement from 8.6 to 8.5 git-gui--askpass: coerce answers to UTF-8 on Windows git-gui: fix error popup when doing blame -> "Show History Context" git-gui: add missing close bracket git-gui: update German translation git-gui: extend translation glossary template with more terms git-gui: update pot template and German translation to current source code
		
			
				
	
	
		
			159 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Tcl
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Tcl
		
	
	
	
	
	
# Simple Chord for Tcl
 | 
						|
#
 | 
						|
# A "chord" is a method with more than one entrypoint and only one body, such
 | 
						|
# that the body runs only once all the entrypoints have been called by
 | 
						|
# different asynchronous tasks. In this implementation, the chord is defined
 | 
						|
# dynamically for each invocation. A SimpleChord object is created, supplying
 | 
						|
# body script to be run when the chord is completed, and then one or more notes
 | 
						|
# are added to the chord. Each note can be called like a proc, and returns
 | 
						|
# immediately if the chord isn't yet complete. When the last remaining note is
 | 
						|
# called, the body runs before the note returns.
 | 
						|
#
 | 
						|
# The SimpleChord class has a constructor that takes the body script, and a
 | 
						|
# method add_note that returns a note object. Since the body script does not
 | 
						|
# run in the context of the procedure that defined it, a mechanism is provided
 | 
						|
# for injecting variables into the chord for use by the body script. The
 | 
						|
# activation of a note is idempotent; multiple calls have the same effect as
 | 
						|
# a simple call.
 | 
						|
#
 | 
						|
# If you are invoking asynchronous operations with chord notes as completion
 | 
						|
# callbacks, and there is a possibility that earlier operations could complete
 | 
						|
# before later ones are started, it is a good practice to create a "common"
 | 
						|
# note on the chord that prevents it from being complete until you're certain
 | 
						|
# you've added all the notes you need.
 | 
						|
#
 | 
						|
# Example:
 | 
						|
#
 | 
						|
#   # Turn off the UI while running a couple of async operations.
 | 
						|
#   lock_ui
 | 
						|
#
 | 
						|
#   set chord [SimpleChord::new {
 | 
						|
#     unlock_ui
 | 
						|
#     # Note: $notice here is not referenced in the calling scope
 | 
						|
#     if {$notice} { info_popup $notice }
 | 
						|
#   }
 | 
						|
#
 | 
						|
#   # Configure a note to keep the chord from completing until
 | 
						|
#   # all operations have been initiated.
 | 
						|
#   set common_note [$chord add_note]
 | 
						|
#
 | 
						|
#   # Activate notes in 'after' callbacks to other operations
 | 
						|
#   set newnote [$chord add_note]
 | 
						|
#   async_operation $args [list $newnote activate]
 | 
						|
#
 | 
						|
#   # Communicate with the chord body
 | 
						|
#   if {$condition} {
 | 
						|
#     # This sets $notice in the same context that the chord body runs in.
 | 
						|
#     $chord eval { set notice "Something interesting" }
 | 
						|
#   }
 | 
						|
#
 | 
						|
#   # Activate the common note, making the chord eligible to complete
 | 
						|
#   $common_note activate
 | 
						|
#
 | 
						|
# At this point, the chord will complete at some unknown point in the future.
 | 
						|
# The common note might have been the first note activated, or the async
 | 
						|
# operations might have completed synchronously and the common note is the
 | 
						|
# last one, completing the chord before this code finishes, or anything in
 | 
						|
# between. The purpose of the chord is to not have to worry about the order.
 | 
						|
 | 
						|
# SimpleChord class:
 | 
						|
#   Represents a procedure that conceptually has multiple entrypoints that must
 | 
						|
#   all be called before the procedure executes. Each entrypoint is called a
 | 
						|
#   "note". The chord is only "completed" when all the notes are "activated".
 | 
						|
class SimpleChord {
 | 
						|
	field notes
 | 
						|
	field body
 | 
						|
	field is_completed
 | 
						|
	field eval_ns
 | 
						|
 | 
						|
	# Constructor:
 | 
						|
	#   set chord [SimpleChord::new {body}]
 | 
						|
	#     Creates a new chord object with the specified body script. The
 | 
						|
	#     body script is evaluated at most once, when a note is activated
 | 
						|
	#     and the chord has no other non-activated notes.
 | 
						|
	constructor new {i_body} {
 | 
						|
		set notes [list]
 | 
						|
		set body $i_body
 | 
						|
		set is_completed 0
 | 
						|
		set eval_ns "[namespace qualifiers $this]::eval"
 | 
						|
		return $this
 | 
						|
	}
 | 
						|
 | 
						|
	# Method:
 | 
						|
	#   $chord eval {script}
 | 
						|
	#     Runs the specified script in the same context (namespace) in which
 | 
						|
	#     the chord body will be evaluated. This can be used to set variable
 | 
						|
	#     values for the chord body to use.
 | 
						|
	method eval {script} {
 | 
						|
		namespace eval $eval_ns $script
 | 
						|
	}
 | 
						|
 | 
						|
	# Method:
 | 
						|
	#   set note [$chord add_note]
 | 
						|
	#     Adds a new note to the chord, an instance of ChordNote. Raises an
 | 
						|
	#     error if the chord is already completed, otherwise the chord is
 | 
						|
	#     updated so that the new note must also be activated before the
 | 
						|
	#     body is evaluated.
 | 
						|
	method add_note {} {
 | 
						|
		if {$is_completed} { error "Cannot add a note to a completed chord" }
 | 
						|
 | 
						|
		set note [ChordNote::new $this]
 | 
						|
 | 
						|
		lappend notes $note
 | 
						|
 | 
						|
		return $note
 | 
						|
	}
 | 
						|
 | 
						|
	# This method is for internal use only and is intentionally undocumented.
 | 
						|
	method notify_note_activation {} {
 | 
						|
		if {!$is_completed} {
 | 
						|
			foreach note $notes {
 | 
						|
				if {![$note is_activated]} { return }
 | 
						|
			}
 | 
						|
 | 
						|
			set is_completed 1
 | 
						|
 | 
						|
			namespace eval $eval_ns $body
 | 
						|
			delete_this
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
# ChordNote class:
 | 
						|
#   Represents a note within a chord, providing a way to activate it. When the
 | 
						|
#   final note of the chord is activated (this can be any note in the chord,
 | 
						|
#   with all other notes already previously activated in any order), the chord's
 | 
						|
#   body is evaluated.
 | 
						|
class ChordNote {
 | 
						|
	field chord
 | 
						|
	field is_activated
 | 
						|
 | 
						|
	# Constructor:
 | 
						|
	#   Instances of ChordNote are created internally by calling add_note on
 | 
						|
	#   SimpleChord objects.
 | 
						|
	constructor new {c} {
 | 
						|
		set chord $c
 | 
						|
		set is_activated 0
 | 
						|
		return $this
 | 
						|
	}
 | 
						|
 | 
						|
	# Method:
 | 
						|
	#   [$note is_activated]
 | 
						|
	#     Returns true if this note has already been activated.
 | 
						|
	method is_activated {} {
 | 
						|
		return $is_activated
 | 
						|
	}
 | 
						|
 | 
						|
	# Method:
 | 
						|
	#   $note activate
 | 
						|
	#     Activates the note, if it has not already been activated, and
 | 
						|
	#     completes the chord if there are no other notes awaiting
 | 
						|
	#     activation. Subsequent calls will have no further effect.
 | 
						|
	method activate {} {
 | 
						|
		if {!$is_activated} {
 | 
						|
			set is_activated 1
 | 
						|
			$chord notify_note_activation
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |