Skip to content

Instantly share code, notes, and snippets.

@fractaledmind
Last active December 20, 2015 04:08
Show Gist options
  • Save fractaledmind/6068139 to your computer and use it in GitHub Desktop.
Save fractaledmind/6068139 to your computer and use it in GitHub Desktop.
This script allows you to handle, one-by-one, changes in your text document put in CriticMarkup format. All 5 forms of CriticMarkup syntax are supported: Add, Delete, Substitute, Comment, Highlight. For each instance of an Addition or Deletion, you can either Accept or Reject the proposed option. For each instance of Substitutions, you can chose…
(* ACCEPT/REJECT CRITICMARKUP CHANGES
--Stephen Margheim
--23 July 2013
--open source
VERSION 1.1
--added if, then, else blocks for all prompts, allowing for script to function even if not all markup forms are present
--added try block to getContext Handler, allowing script to function when context range exceeds beginning or end of doc
This script takes as input a text with CriticMarkup in it and allows the user to either Accept or Reject those changes. To begin, select your text and copy it to the clipboard by cutting it. Then run the script.
This script runs with TextMate. If you use another text editor, you will need to change the output portion of the script.
For Additions, Deletions, and Substitutions, the script asks if you want to Accept or Reject. Based on what you choose, it will replace the commented text with regular text.
For Comments and Highlights, the script asks you what you want to insert given that comment. If you simply press "Enter" without typing anything, the script will delete the comment without adding anything. If you type something in, it will replace the comment with that text.
Every dialog box contains Context for you to see, without returning to the text, where the Markup is and how it relates to the sentences around it.
NOTE: I have changed the syntax for Comments for ease of use. Now, Comments use {^^…^^}, while Highlights use {==…==}{<<…>>}. In the original CriticMarkup syntax both use {<<…>>} for the Comment sections. This made distinguishing Comments from Highlights too difficult, however.
*)
(* PART ONE:
Get the CriticMarkup Information
*)
--get text as list divided by character
set tid to AppleScript's text item delimiters
set inputText to the clipboard
set AppleScript's text item delimiters to ""
set inputList to text items of inputText
set AppleScript's text item delimiters to tid
--get all CriticMarkup info
set theAdditions to my extractBetween(inputText, "{++", "++}")
set theDeletions to my extractBetween(inputText, "{--", "--}")
set theSubstitutions to my extractBetween(inputText, "{~~", "~~}")
set theComments to my extractBetween(inputText, "{^^", "^^}")
set theHighlights to my extractBetween(inputText, "{==", "<<}")
set theHighlightText to my extractBetween(inputText, "{==", "==}")
set theHighlightComment to my extractBetween(inputText, "==}{>>", "<<}")
--prepare CriticMarkdup info for Find and Replace
set AddsList to my rebindList(theAdditions, "{++", "++}")
set DelsList to my rebindList(theDeletions, "{--", "--}")
set SubsList to my rebindList(theSubstitutions, "{~~", "~~}")
set CommsList to my rebindList(theComments, "{^^", "^^}")
set HighlightList to my rebindList(theHighlights, "{==", "<<}")
--get text as new list divided by words
set AppleScript's text item delimiters to " "
set theText_list to text items of inputText
set AppleScript's text item delimiters to tid
--prepare lists for items below
set MatchedItems_adds to {}
set MatchedItems_dels to {}
set MatchedItems_subs to {}
set MatchedItems_comms to {}
set MatchedItems_highs to {}
--get all instances of CriticMarkup in text, divided by kind
repeat with i from 1 to count of theText_list
if item i of theText_list contains "{++" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_adds
else if item i of theText_list contains "{--" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_dels
else if item i of theText_list contains "{~~" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_subs
else if item i of theText_list contains "{^^" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_comms
else if item i of theText_list contains "^^}" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_comms
else if item i of theText_list contains "{==" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_highs
else if item i of theText_list contains "<<}" then
set MatchedItem to item i of theText_list
copy MatchedItem to end of MatchedItems_highs
end if
end repeat
--get index of CriticMarkup in text, divided by kind
set AddsIndex to my getIndex(MatchedItems_adds, theText_list)
set DelsIndex to my getIndex(MatchedItems_dels, theText_list)
set SubsIndex to my getIndex(MatchedItems_subs, theText_list)
set CommsIndex to my getIndex(MatchedItems_comms, theText_list)
set HighsIndex to my getIndex(MatchedItems_highs, theText_list)
(* PART TWO:
Get contextual data for all CriticMarkup
*)
--get contextual text of CriticMarkup in text, divided by kind
set AddsContext to my getContext(AddsIndex, theText_list)
set DelsContext to my getContext(DelsIndex, theText_list)
set SubsContext to my getContext(SubsIndex, theText_list)
set CommsContext to my getContext_HighsorComms(CommsIndex, theText_list)
set HighsContext to my getContext_HighsorComms(HighsIndex, theText_list)
(* PART THREE
Accept or Reject Additions, Deletions, Substitutions, Comments, and Highlights
*)
if (not (AddsContext is {})) then
set AddsPrompt to my promptUser(AddsContext, AddsIndex, theText_list, AddsList, theAdditions, inputText)
else
set AddsPrompt to inputText
end if
if (not (DelsContext is {})) then
set DelsPrompt to my promptUser_dels(DelsContext, DelsIndex, theText_list, DelsList, theDeletions, AddsPrompt)
else
set DelsPrompt to AddsPrompt
end if
if (not (SubsContext is {})) then
set theSubs to my getSubs_list(theSubstitutions)
set theDeSubs to my getDeSubs_list(theSubstitutions)
set theSubsPrompt to my promptUser_sub(SubsContext, SubsIndex, theText_list, SubsList, theSubs, theDeSubs, DelsPrompt)
else
set theSubsPrompt to DelsPrompt
end if
if (not (CommsContext is {})) then
set theCommsPrompt to my promptUser_Comms(CommsContext, CommsList, theSubsPrompt)
else
set theCommsPrompt to theSubsPrompt
end if
if (not (HighsContext is {})) then
set theHighsPrompt to my promptUser_Highs(HighsContext, HighlightList, theHighlightText, theCommsPrompt)
else
set theHighsPrompt to theCommsPrompt
end if
(* PART FOUR
Insert new text into TextMate
*)
set FinalText to theHighsPrompt
set the clipboard to FinalText
delay 0.1
tell application "TextMate"
activate
tell application "System Events"
delay 0.1
keystroke "v" using {command down}
end tell
end tell
(* HANDLERS *)
to extractBetween(SearchText, startText, endText)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to startText
set liste to text items of SearchText
set AppleScript's text item delimiters to endText
set extracts to {}
repeat with subText in liste
if subText contains endText then
copy text item 1 of subText to end of extracts
end if
end repeat
set AppleScript's text item delimiters to tid
return extracts
end extractBetween
on getSubs_list(theSubstitutions)
set theSubs to {}
set theDeSubs to {}
repeat with i from 1 to count of theSubstitutions
set theSub to item i of theSubstitutions as string
set theDeSub to my getDeSubs(theSub)
set theSub to my getSubs(theSub)
copy theDeSub to end of theDeSubs
copy theSub to end of theSubs
end repeat
return theSubs
end getSubs_list
on getDeSubs_list(theSubstitutions)
set theSubs to {}
set theDeSubs to {}
repeat with i from 1 to count of theSubstitutions
set theSub to item i of theSubstitutions as string
set theDeSub to my getDeSubs(theSub)
set theSub to my getSubs(theSub)
copy theDeSub to end of theDeSubs
copy theSub to end of theSubs
end repeat
return theDeSubs
end getDeSubs_list
on rebindList(theExtracts, front_chars, end_chars)
set theboundList to {}
repeat with i from 1 to count of theExtracts
set theExtract to item i of theExtracts
set boundText to front_chars & theExtract & end_chars
copy boundText to the end of theboundList
end repeat
return theboundList
end rebindList
on getIndex(MatchedItems, theText_list)
set MatchedItems_index to {}
repeat with i from 1 to count of MatchedItems
set theItem to item i of MatchedItems
set matchedIndex to my list_position(theItem, theText_list)
if matchedIndex is not equal to 0 then
copy matchedIndex to end of MatchedItems_index
end if
end repeat
return MatchedItems_index
end getIndex
on getContext(indexes, theList)
set tid to AppleScript's text item delimiters
set contextTexts to {}
repeat with i from 1 to count of indexes
set thisIndex to item i of indexes
set priorWords to {}
set afterWords to {}
repeat with i from 1 to 15
try
set priorWord to item (thisIndex + -i) of theList
copy priorWord to beginning of priorWords
set afterWord to item (thisIndex + i) of theList
copy afterWord to end of afterWords
end try
end repeat
set AppleScript's text item delimiters to " "
set priorWords to priorWords as string
set afterWords to afterWords as string
set AppleScript's text item delimiters to tid
set theWord to item thisIndex of theList
set contextText to priorWords & " " & theWord & " " & afterWords
copy contextText to end of contextTexts
end repeat
return contextTexts
end getContext
on getContext_HighsorComms(indexes, theList)
set tid to AppleScript's text item delimiters
set Index_grouped to my groupList(indexes, 2)
set contextTexts to {}
repeat with i from 1 to count of Index_grouped
set theIndexes to item i of Index_grouped
set thisIndex_front to item 1 of theIndexes
set thisIndex_back to item 2 of theIndexes
set priorWords to {}
set afterWords to {}
repeat with i from 1 to 10
set priorWord to item (thisIndex_front + -i) of theList
copy priorWord to beginning of priorWords
set afterWord to item (thisIndex_back + i) of theList
copy afterWord to end of afterWords
end repeat
set AppleScript's text item delimiters to " "
set priorWords to priorWords as string
set afterWords to afterWords as string
set theComm_list to items thisIndex_front thru thisIndex_back of theList
set theComm to theComm_list as string
set tid to AppleScript's text item delimiters
set contextText to priorWords & " " & theComm & " " & afterWords
copy contextText to end of contextTexts
end repeat
return contextTexts
end getContext_HighsorComms
on promptUser(theContext, theIndex, theList, findList, replaceList, inputText)
repeat with i from 1 to count of theContext
set context to item i of theContext
set thisIndex to item i of theIndex
set theWord to item thisIndex of theList
display dialog "Accept or Reject this change:" & return & tab & theWord & return & return & "Context: " & return & "\"" & context & "\"" buttons {"Accept", "Reject"} with title {"Accept or Reject CriticMarkup Changes"} default button {"Accept"}
if result = {button returned:"Accept"} then
set theFind to item i of findList
set theReplace to item i of replaceList
set inputText to my find_replace(inputText, theFind, theReplace)
else if result = {button returned:"Reject"} then
set theFind to item i of findList
set inputText to my find_replace(inputText, theFind, "")
end if
end repeat
return inputText
end promptUser
on promptUser_dels(theContext, theIndex, theList, findList, replaceList, inputText)
repeat with i from 1 to count of theContext
set context to item i of theContext
set theWord to item i of findList
display dialog "Accept or Reject this change:" & return & tab & theWord & return & return & "Context: " & return & "\"" & context & "\"" buttons {"Accept", "Reject"} with title {"Accept or Reject CriticMarkup Changes"} default button {"Accept"}
if result = {button returned:"Accept"} then
set theFind to item i of findList
set theReplace to ""
set inputText to my find_replace(inputText, theFind, theReplace)
else if result = {button returned:"Reject"} then
set theFind to item i of findList
set theReplace to item i of replaceList
set inputText to my find_replace(inputText, theFind, theReplace)
end if
end repeat
return inputText
end promptUser_dels
on promptUser_sub(theContext, theIndex, theList, findList, replaceList_acc, replaceList_rej, inputText)
repeat with i from 1 to count of theContext
set context to item i of theContext
set thisIndex to item i of theIndex
set theWord to item thisIndex of theList
display dialog "Accept or Reject this change:" & return & tab & theWord & return & return & "Context: " & return & "\"" & context & "\"" buttons {"Accept", "Reject"} with title {"Accept or Reject CriticMarkup Changes"} default button {"Accept"}
if result = {button returned:"Accept"} then
set theFind to item i of findList
set theReplace to item i of replaceList_acc
set inputText to my find_replace(inputText, theFind, theReplace)
else if result = {button returned:"Reject"} then
set theFind to item i of findList
set theReplace to item i of replaceList_rej
set inputText to my find_replace(inputText, theFind, theReplace)
end if
end repeat
return inputText
end promptUser_sub
on promptUser_Comms(theContext, findList, inputText)
repeat with i from 1 to count of theContext
set theComm to item i of findList
set context to item i of theContext
set thePrompt to display dialog "Note this Comment:" & return & tab & theComm & return & return & "Context: " & return & "\"" & context & "\"" & return & return & "What do you wish to insert?" & return default answer ""
set theReplace to text returned of thePrompt
set theFind to item i of findList
set inputText to my find_replace(inputText, theFind, theReplace)
end repeat
end promptUser_Comms
on promptUser_Highs(theContext, findList, replaceList_rej, inputText)
repeat with i from 1 to count of theContext
set theComm to item i of findList
set context to item i of theContext
set thePrompt to display dialog "Note this Highlighted Comment:" & return & tab & theComm & return & return & "Context: " & return & "\"" & context & "\"" & return & return & "What do you wish to insert?" & return default answer ""
set theReplace to text returned of thePrompt
if theReplace is not "" then
set theFind to item i of findList
set inputText to my find_replace(inputText, theFind, theReplace)
else if theReplace is "" then
set theFind to item i of findList
set theReplace to item i of replaceList_rej
set inputText to my find_replace(inputText, theFind, theReplace)
end if
end repeat
end promptUser_Highs
(* SUB-ROUTINES *)
to getSubs(theText)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "~>"
set theDelItem to text item 1 of theText
set theSubItem to text item 2 of theText
set AppleScript's text item delimiters to tid
return theSubItem as list
end getSubs
on getDeSubs(theText)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "~>"
set theDelItem to text item 1 of theText
set theSubItem to text item 2 of theText
set AppleScript's text item delimiters to tid
return theDelItem as list
end getDeSubs
on list_position(this_item, this_list)
repeat with i from 1 to the count of this_list
if item i of this_list is this_item then return i
end repeat
return 0
end list_position
on find_replace(the_string, search_strings, replace_strings)
set ListNumber to the (count of search_strings) as number
set OldDelims to AppleScript's text item delimiters
considering case
set AppleScript's text item delimiters to search_strings
set newText to text items of the_string
set AppleScript's text item delimiters to replace_strings
set the_string to newText as text
set AppleScript's text item delimiters to OldDelims
end considering
return the_string
end find_replace
on groupList(lst, groupLen)
-- HAS (http://applemods.sourceforge.net/mods/Data/List.php)
local lst, tailLen, groupLen, idx
try
if lst's class is not list then error "not a list." number -1704
script k
property l : lst
property res : {}
end script
set tailLen to (count of k's l) mod groupLen
repeat with idx from 1 to ((count of k's l) - tailLen) by groupLen
set k's res's end to k's l's items idx thru (idx + groupLen - 1)
end repeat
if tailLen is not 0 then
set k's res's end to k's l's items -tailLen thru -1
end if
return k's res
on error eMsg number eNum
error "Can't groupList: " & eMsg number eNum
end try
end groupList
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment