Back in 2017, I wrote a post about a script that would align outline text, which would insert spacers into a formatted outline document to make it consistently spaced. This script has been in use for a few years by a few readers of this blog, and it generally works pretty well, but earlier this week I got a question about it. If a document is otherwise nicely formatted, and you only want to align a section of the document, running the script on an entire document is probably a bad idea, because there is always the possibility of it changing something that already looks pretty good. The question, then, was asking if it was possible to modify this script to run on a portion of the document, instead of the whole document.
Friday, April 24. 2020
LDC #169: Adding New Functionality To Older Scripts
Any readers who have done development work will understand what a loaded question something like that can be. It’s a relatively simple sounding change, but the script was written almost 3 years ago... at this point I no longer remember how it works, and tweaking it to do something different can be problematic. Reading through all the code, reading the comments and reviewing the previous blog posts would all be good places to start, but fortunately Legato is flexible enough that I really don’t need to know how it works, all I need to do is to figure out where to change it.
This script has 6 functions, setup, run, main, get_font_tag, get_word_length, replace_in_string, and test_nbsp. Of these, only the run function is going to need to be changed. The other 5 are either structural to set up the script to run, or utility to help the script while it’s running. I don’t want to change the functionality at its core; I just want to change the scope that the function runs on. So right away, that narrows our focus from a couple hundred lines of code to just 70 lines that we need to add to or modify, which is much less daunting of a task.
Like most HTML parser scripts that we have discussed on this blog post, this one has a central while loop at its heart that iterates until it hits the end of the list of elements (and then at that point it can simply exit). In order to have the script work on a section of text, we’re going to need to change where that loop starts and where it ends. To change where it starts, we can test to see if the user has a linear selection of text, and if so, we can grab the start of the text selection, and move the parser back one element from that point, and use that as our starting element to search. To end the loop early, we can just put an additional test in the loop, to check to see if our current element coordinates are beyond the coordinates of the selection, and if so it can just return. That doesn’t sound very difficult. At this point then, without figuring out how this script actually works, I’ve decided on a workable plan to modify it very quickly to fit some new needs. Let’s take a look at the modified run function:
/****************************************/ int run(int f_id, string mode, handle edit_window) { /* Call from Hook Processor */ /****************************************/ <omitted sections of old code> edit_object = GetEditObject(edit_window); /* create edit object */ sgml = SGMLCreate(edit_object); /* create sgml object */ element = SGMLNextElement(sgml); /* get the first sgml element */ ey1 = 0; /* initialize ex1 */ /* new */ smode = GetSelectMode(edit_object); /* get selection mode */ /* new */ if(smode == EDO_LINEAR_SELECT){ /* if linear */ /* new */ sx1 = GetSelectStartXPosition(edit_object); /* get start x pos */ /* new */ sy1 = GetSelectStartYPosition(edit_object); /* get start y pos */ /* new */ ex1 = GetSelectEndXPosition(edit_object); /* get end x pos */ /* new */ ey1 = GetSelectEndYPosition(edit_object); /* get end y pos */ /* new */ element = SGMLPreviousElement(sgml,sx1,sy1); /* get element prior to selection */ /* new */ } /* */ /* new */ while(element != ""){ /* while element isn't empty */ if (IsError(element)){ /* if it couldn't read the element */ MessageBox('x',"Could not read HTML element, aborting."); /* print error */ return ERROR_EXIT; /* return error */ } /* */ if (FindInString(element, "<p", 0, false)&rt;(-1)){ /* if the element is a paragraph */ matches = true; /* reset matches */ sx = SGMLGetItemPosSX(sgml); /* get sgml start */ sy = SGMLGetItemPosSY(sgml); /* get sgml start */ ex = SGMLGetItemPosEX(sgml); /* set sgml end */ ey = SGMLGetItemPosEY(sgml); /* set sgml end */ if(ey &rt; ey1 && ey1 !=0){ /* if ey1 is set, and we're beyond it */ /* new */ break; /* break the loop */ /* new */ } /* */ /* new */ switch (matches){ /* switch on boolean matches */ case matches = test_nbsp(sx, sy, sgml, edit_object): /* try case 2 */ break; /* end */ default: /* if nothing happened */ SGMLSetPosition(sgml, ex, ey); /* set to end of item */ break; /* */ } /* */ } /* */ element = SGMLNextElement(sgml); /* get the next sgml element */ } /* */ CloseHandle(edit_object); /* close edit object */ MessageBox('i',"Found and modified %d paragraphs.",counter); /* messagebox */ return ERROR_NONE; /* Exit Done */ } /* end setup */
The new code is very simple and has been highlighted in red. We get the selection mode of the Edit Object, and if it is a linear selection, we get the start x and start y, as well as end x and end y coordinates. We get the element at the start with SGMLPreviousElement, and in our main while loop, after we get the coordinates of a paragraph we’re going to modify, we test to see if it’s beyond the scope of our selection. If so, we simply break the loop. The previous functionality is preserved, since we want the script to execute the way it used to if a user doesn’t have a selection. If you do have a selection, the new break will trigger before unwanted paragraphs are modified.
Here is the complete script without commentary:
// // // GoFiler Legato Script - Align Outlined Text // ------------------------------------------ // // Rev 06/30/2017 // 08/16/2017 // 08/17/2018 // // // (c) 2017 Novaworks, LLC -- All rights reserved. // // Examines any HTML file for a paragraph followed by up to 5 words, followed by a tab or 5+ non-breaking // spaces. If it finds them, it wraps the initial words in a font tag and deletes the non-breaking // spaces. // /********************************************************/ /* Global Items */ /* ------------ */ /********************************************************/ #define NBSP "(( )|(&NBSP;)|( )|( ))" #define NBSP_ONLY "^"+NBSP+"{5,}$" #define TAB_CHAR "	" /* font tags for SM, MD, and LG values defined below */ #define FONT_TAG_SM "<FONT STYLE=\"display: inline-block; width: 0.5in; float: left; white-space:nowrap\"&rt;" #define FONT_TAG_MD "<FONT STYLE=\"display: inline-block; width: 1in; float: left; white-space:nowrap\"&rt;" #define FONT_TAG_LG "<FONT STYLE=\"display: inline-block; width: 1.5in; float: left; white-space:nowrap\"&rt;" #define UNDERLINE_TAG "<U&rt;" #define UNDERLINE_CLOSE "</U&rt;" #define CLOSE_FONT "</FONT&rt;" /* threshold is used with CHARS_SM,CHARS_MD,CHARS_LG to */ /* * calculate font tag to use. */ #define CHAR_SIZE_THRESHOLD 8 /* threshold for character levels */ #define CHARS_SM 0 /* small */ #define CHARS_MD 1 /* medium */ #define CHARS_LG 2 /* large */ #define MAX_LEAD_IN_WORDS 3 /* max words in a lead-in segment */ #define MIN_NBSP_AFTER_LEAD_IN 5 /* minimum NBSP's after lead in */ int run (int f_id, string mode, handle edit_window); int setup (); string get_font_tag (int text_width); int get_word_length (string test); string replace_in_string (string data, string find, string replace); boolean test_nbsp (int psx, int psy, handle sgml, handle edit_object); int counter; /****************************************/ int setup() { /* Called from Application Startup */ /****************************************/ string fnScript; /* Us */ string item[10]; /* Menu Item */ int rc; /* Return Code */ /* */ /* ** Add Menu Item */ /* * Define Function */ item["Code"] = "EXTENSION_ALIGN_OUTLINE"; /* Function Code */ item["MenuText"] = "&Align Outline Text"; /* Menu Text */ item["Description"] = "<B&rt;Align Outline Text</B&rt; "; /* Description (long) */ item["Description"]+= "\r\rBreaks outline out into aligned blocks.";/* * description */ item["SmallBitmap"] = "HTML_ALIGN_OUTLINE"; /* * Check for Existing */ rc = MenuFindFunctionID(item["Code"]); /* Look for existing */ if (IsNotError(rc)) { /* Was already be added */ return ERROR_NONE; /* Exit */ } /* end error */ /* * Registration */ rc = MenuAddFunction(item); /* Add the item */ if (IsError(rc)) { /* Was already be added */ return ERROR_NONE; /* Exit */ } /* end error */ fnScript = GetScriptFilename(); /* Get the script filename */ MenuSetHook(item["Code"], fnScript, "run"); /* Set the Test Hook */ return ERROR_NONE; /* Return value (does not matter) */ } /* end setup */ /****************************************/ int main() { /* Initialize from Hook Processor */ /****************************************/ handle window; /* window handle */ string windows[][]; /* list of all windows */ int size; /* size of edit window list */ int ix; /* counter */ /* */ if (GetScriptParent()!="LegatoIDE"){ /* if not running in IDE */ return ERROR_NONE; /* return */ } /* */ setup(); /* Add to the menu */ windows = EnumerateEditWindows(); /* get all edit windows */ size = ArrayGetAxisDepth(windows); /* get size of windows */ for(ix=0;ix<size;ix++){ /* for each edit window */ if (GetExtension(windows[ix]["Filename"])==".htm"){ /* if it's an HTML file */ MessageBox("editing window: %s",windows[ix]["Filename"]); /* display message */ window = MakeHandle(windows[ix]["ClientHandle"]); /* get handle to html file */ run(0,"preprocess",window); /* run the function */ return ERROR_NONE; /* return */ } /* */ } /* */ return ERROR_NONE; /* return */ } /* end setup */ /****************************************/ int run(int f_id, string mode, handle edit_window) { /* Call from Hook Processor */ /****************************************/ int px,py; /* start pos of paragraph text */ int text_width; /* the width of the lead in text */ int ex,ex1,ey1,ey,sx1,sx,sy1,sy; /* positional variables */ /* new */ boolean matches; /* if matches a replace pattern */ dword type; /* type of window */ string font_tag; /* font tag to write */ string content; /* content of an SGML tag */ string closetag; /* closing tag to write out */ string element; /* sgml element */ handle sgml; /* sgml object */ int smode; /* selection mode */ handle edit_object; /* edit object */ string text; /* closing element of sgml object */ /* */ if (mode!="preprocess"){ /* if mode is not preprocess */ return ERROR_NONE; /* return no error */ } /* */ counter = 0; /* reset counter from last run */ if (edit_window==NULL_HANDLE){ /* if not passed a handle, get one */ edit_window = GetActiveEditWindow(); /* get handle to edit window */ } /* */ if(IsError(edit_window)){ /* get active edit window */ MessageBox('x',"Cannot get edit window."); /* display error */ return ERROR_EXIT; /* return */ } /* */ type = GetEditWindowType(edit_window) & EDX_TYPE_ID_MASK; /* get the type of the window */ if (type!=EDX_TYPE_PSG_PAGE_VIEW && type!=EDX_TYPE_PSG_TEXT_VIEW){ /* and make sure type is HTML or Code */ MessageBox('x',"This is not an HTML edit window."); /* display error */ return ERROR_EXIT; /* return error */ } /* */ edit_object = GetEditObject(edit_window); /* create edit object */ sgml = SGMLCreate(edit_object); /* create sgml object */ element = SGMLNextElement(sgml); /* get the first sgml element */ ey1 = 0; /* initialize ex1 */ smode = GetSelectMode(edit_object); /* get selection mode */ /* new */ if(smode == EDO_LINEAR_SELECT){ /* if linear */ /* new */ sx1 = GetSelectStartXPosition(edit_object); /* get start x pos */ /* new */ sy1 = GetSelectStartYPosition(edit_object); /* get start y pos */ /* new */ ex1 = GetSelectEndXPosition(edit_object); /* get end x pos */ /* new */ ey1 = GetSelectEndYPosition(edit_object); /* get end y pos */ /* new */ element = SGMLPreviousElement(sgml,sx1,sy1); /* get element prior to selection */ /* new */ } /* */ /* new */ while(element != ""){ /* while element isn't empty */ if (IsError(element)){ /* if it couldn't read the element */ MessageBox('x',"Could not read HTML element, aborting."); /* print error */ return ERROR_EXIT; /* return error */ } /* */ if (FindInString(element, "<p", 0, false)&rt;(-1)){ /* if the element is a paragraph */ matches = true; /* reset matches */ sx = SGMLGetItemPosSX(sgml); /* get sgml start */ sy = SGMLGetItemPosSY(sgml); /* get sgml start */ ex = SGMLGetItemPosEX(sgml); /* set sgml end */ ey = SGMLGetItemPosEY(sgml); /* set sgml end */ if(ey &rt; ey1 && ey1 !=0){ /* if ey1 is set, and we're beyond it */ /* new */ break; /* break the loop */ /* new */ } /* */ /* new */ switch (matches){ /* switch on boolean matches */ case matches = test_nbsp(sx, sy, sgml, edit_object): /* try case 2 */ break; /* end */ default: /* if nothing happened */ SGMLSetPosition(sgml, ex, ey); /* set to end of item */ break; /* */ } /* */ } /* */ element = SGMLNextElement(sgml); /* get the next sgml element */ } /* */ CloseHandle(edit_object); /* close edit object */ MessageBox('i',"Found and modified %d paragraphs.",counter); /* messagebox */ return ERROR_NONE; /* Exit Done */ } /* end setup */ /****************************************/ boolean test_nbsp(int psx, int psy, handle sgml, handle edit_object){ /* test if only NBSP's in after leadin */ /****************************************/ handle wp; /* word parser */ int lct; /* lead in count */ int nct; /* nbsp count */ int sx,sy; /* startponits of paragraph content */ int ex,ey; /* endpoints of paragraph content */ int text_width; /* width of lead in text */ boolean is_tab; /* true if dealing with tab char */ boolean ended_lead_in; /* has lead in ended? */ boolean last_word_nbsp; /* was last char nbsp? */ boolean is_nbsp; /* is this char an nbsp? */ boolean underline; /* true if underlining */ string u_tag; /* underline tag */ string u_close; /* close underline tag */ string tag_type; /* type of tag to look for */ string font_tag; /* font tag to use */ string last_word; /* the last word ` */ string lead_in; /* lead in string */ string nbsp_string; /* nbsp_string */ string wp_word; /* parsed word */ string content; /* content of paragraph */ string new_lead_in; /* new lead-in wrapped in font tags */ string element; /* next element */ /* */ SGMLSetPosition(sgml,psx,psy); /* reset to start of paragraph */ element = SGMLNextElement(sgml); /* get next SGML element */ sx = SGMLGetItemPosEX(sgml); /* get end of P tag */ sy = SGMLGetItemPosEY(sgml); /* get end of P tag */ content = SGMLFindClosingElement(sgml, SP_FCE_CODE_AS_IS); /* get content of paragraph */ ex = SGMLGetItemPosSX(sgml); /* get start of close tag */ ey = SGMLGetItemPosSY(sgml); /* get start of close tag */ wp = WordParseCreate(WP_SGML_TAG,content); /* create parser for content of para */ wp_word = WordParseGetWord(wp); /* get next word */ while(wp_word!=""){ /* while we have a next word */ if (IsRegexMatch(wp_word,NBSP)){ /* test if nbsp */ is_nbsp = true; /* its a nbsp */ } /* */ else{ /* if it's not an nbsp */ if (wp_word==TAB_CHAR){ /* if it's a tab char */ is_tab = true; /* remember we've got a tab */ } /* */ is_nbsp = false; /* store value */ } /* */ if (IsSGMLTag(wp_word)==false){ /* if it's not an SGML tag */ if (!ended_lead_in){ /* if lead in hasn't ended */ if (is_tab){ /* if it's a tab character */ nbsp_string = wp_word; /* add char to space string */ nct = 5; /* counts as 5 spaces */ break; /* stop processing words */ } /* */ if (last_word_nbsp == true && is_nbsp == false){ /* if last word was nbsp, but not eoli */ lead_in = GetStringSegment(lead_in,0, /* remove extra space from lead-in */ GetStringLength(lead_in)-1); /* * remove extra space */ lead_in += last_word + wp_word; /* add last word and this word to string*/ text_width+=get_word_length(last_word)+ /* add last_word width to width */ get_word_length(wp_word); /* add wp_word width to width */ last_word = ""; /* reset last word */ last_word_nbsp = false; /* reset last word nbsp */ lct+=2; /* increment lead in counter by 2 */ } /* */ else { /* */ if (last_word_nbsp == false && is_nbsp == false){ /* if neither word was nbsp */ lead_in += wp_word+" "; /* add word to lead_in */ text_width+= get_word_length(wp_word); /* add length of word to word length */ lct ++; /* increment lead in word count */ } /* */ } /* */ if(last_word_nbsp == true && is_nbsp){ /* if this and last word were nbsps */ ended_lead_in = true; /* lead in is over */ nbsp_string = last_word + wp_word; /* store as start of nbsp string */ nct = 2; /* non breaking space count is 2 */ last_word = wp_word; /* store word as last word */ } /* */ if (last_word_nbsp == false && is_nbsp){ /* if nbsp but last word was not */ last_word = wp_word; /* store last word */ last_word_nbsp = true; /* remember last word is nbsp */ } /* */ } /* */ else{ /* if lead in has ended */ if (is_nbsp){ /* if it's a nbsp */ nbsp_string += wp_word; /* add to nbsp string */ nct++; /* increment nonbreaking space counter */ } /* */ else{ /* if lead in is over and not nbsp */ break; /* break out of loop */ } /* */ } /* */ } /* */ else{ /* if it is an SGML tag */ if (!ended_lead_in){ /* if still inside lead_in */ if (FindInString(wp_word,"<U")&rt;(-1)){ /* if it's an underline */ underline = true; /* set underlined to true */ } /* */ } /* */ } /* */ wp_word = WordParseGetWord(wp); /* gets the next word */ } /* */ //MessageBox("lead in: (%d)%s",lct,lead_in); if (lct == 0){ /* if no lead-in */ return false; /* return */ } /* */ if ( lct <= MAX_LEAD_IN_WORDS && nct &rt;= MIN_NBSP_AFTER_LEAD_IN){ /* if it meets criteria for lead-in */ font_tag = get_font_tag(text_width); /* get font tag to use */ lead_in = TrimString(lead_in); /* remove leading / trailing spaces */ if (underline){ /* if underlining */ u_tag = UNDERLINE_TAG; /* underline tag to write out */ u_close = UNDERLINE_CLOSE; /* underline close to write out */ } /* */ else{ /* if not underlining */ u_tag = ""; /* blank tag */ u_close = ""; /* blank tag */ } /* */ new_lead_in = font_tag + u_tag + lead_in + u_close + CLOSE_FONT; /* wrap lead in with new font tag */ content = replace_in_string(content,lead_in,new_lead_in); /* replace lead in */ //MessageBox("replacing content."); if (content==""){ /* if nothing was replaced */ return false; /* return false */ } /* */ content = ReplaceInString(content,nbsp_string,""); /* remove nbsp's */ WriteSegment(edit_object,content,sx,sy,ex,ey); /* write segment out */ counter++; /* increment counter */ CloseHandle(wp); /* close handle */ return true; /* return that we did something */ } /* */ CloseHandle(wp); /* close handle */ return false; /* */ } /****************************************/ string get_font_tag(int text_width){ /* return appropriate font tag */ /****************************************/ string font_tag; /* font tag to return */ /* */ switch (text_width/CHAR_SIZE_THRESHOLD){ /* switch on width of lead-in text */ case (CHARS_SM): /* if small */ font_tag = FONT_TAG_SM; /* use small tag */ break; /* break switch */ case (CHARS_MD): /* if medium */ font_tag = FONT_TAG_MD; /* use medium font tag */ break; /* break switch */ case (CHARS_LG): /* if large */ font_tag = FONT_TAG_LG; /* use large font tag */ break; /* break */ default: /* if none of the above */ font_tag = ""; /* do not set a font tag */ break; /* break */ } /* */ return font_tag; /* return selected value */ } /****************************************/ int get_word_length(string test){ /* gets the length of a word */ /****************************************/ if (IsSGMLCharacterEntity(test)){ /* test if it's a character entity */ return 1; /* counts as 1 character */ } /* */ if (IsSGMLTag(test)){ /* if it's an SGML tag */ return 0; /* doesn't render, has zero width */ } /* return true */ return GetStringLength(test); /* return default length of word */ } /****************************************/ string replace_in_string(string data, string find, string replace){ /* replace the first occurance in a str */ /****************************************/ int begin; /* beginning of the replaced segment */ int length; /* length of segment to rpelace */ handle wordparse; /* word parser handle */ string front; /* front part of my new string */ string wp_word; /* word from word parser */ string back; /* back part of my new string */ /* */ length = GetStringLength(find); /* length of segment to replace */ wordparse = WordParseCreate(WP_SGML_TAG,data); /* get word parser */ wp_word = "<init&rt;"; /* initialize wordparser with word */ while(wp_word!="" && IsSGMLTag(wp_word)){ /* advance while word is SGML */ wp_word = WordParseGetWord(wordparse); /* get next word */ if (IsSGMLTag(wp_word)){ /* if the word is an SGML tag */ begin+=GetStringLength(wp_word); /* get length of word */ } /* */ } /* */ begin = FindInString(data,GetStringSegment(find,0,1),begin); /* find start of replace string in data */ if (begin<0){ /* if not found */ return ""; /* return nothing */ } /* */ front = GetStringSegment(data,0,begin); /* get front of new string */ back = GetStringSegment(data,begin+length); /* get back of new string */ return front+replace+back; /* return new string */ } #beginresource HTML_ALIGN_OUTLINE BITMAP { '42 4D 78 06 00 00 00 00 00 00 36 00 00 00 28 00' '00 00 14 00 00 00 14 00 00 00 01 00 20 00 00 00' '00 00 42 06 00 00 12 0B 00 00 12 0B 00 00 00 00' '00 00 00 00 00 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 B8 82 4D 00 B8 82 4D 00 B8 82' '4D 00 B8 82 4D 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 72 72 72 00 FF FF' 'FF 00 FF FF FF 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 72 72 72 00 72 72 72 00 72 72' '72 00 FF FF FF 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 B8 82 4D 00 B8 82 4D 00 B8 82' '4D 00 B8 82 4D 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 72 72 72 00 FF FF' 'FF 00 FF FF FF 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 B8 82 4D 00 B8 82 4D 00 B8 82' '4D 00 B8 82 4D 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 72 72' '72 00 FF FF FF 00 FF FF FF 00 FF FF FF 00 FF FF' 'FF 00 FF FF FF 00 FF FF FF 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 CC 00 FF 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 72 72' '72 00 72 72 72 00 72 72 72 00 72 72 72 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 CC 00 FF 00 CC 00 FF 00 CC 00' 'FF 00 CC 00 FF 00 00 00 ' } #endresource
This is a fairly short blog post, but it illustrates a fairly common task. It’s not often that you have to start from scratch to create a brand new script. Generally, picking up something older and modifying it is much more common. When doing that in Legato (or any language, really) it’s important to try to remember that you want to change as little as possible to introduce your new feature. In this case, I changed 11 lines of code (12 if you count the variable declaration line) and added a new feature to a previous script. Keep that in mind when working on an older script or program, the goal should always be to make a minimally small change to meet your new requirement.
Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC. |
Additional Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato