StrongED:Dynamic generation of scopes

From RISC OS

Jump to: navigation, search

StrongED: dynamic generation of scopes

A common action that is often performed in an editor is to select some text and then apply some transformation to it, be it from within the editor or using some external tool. The downside of such actions is that you have to select the scope explicitly where sometimes it would be useful if the scope could be created on the fly. This article will show ways to do just that.

Some of StrongED's functions already do create their own scope, for example ClickLists. The ability to double-click URLs to launch them is provided by a ClickList and the scope is determined by the search expression that's specified in the ClickList definition. The text matched by the search expression is assigned to the system variable StrongED$Tmp_MarkWord. This variable is then used by the BroadcastURL function to launch the URL.

The downside of the above method is that it can only be used with functions that use the StrongED$Tmp_MarkWord variable, which is only a limited number. Other functions require a scope that's either the entire text or a selected block of text. So, let's see how text can be selected on the fly.

One of the easiest, if somewhat indiscriminate, ways is to use MarkLine to select the entire Textline that the caret is in, eg

 c-Space   MarkLine Replace(" ", "", Block, NoLine, NoCase)

Another way to achieve this is to use the BlockContinuous function in conjuction with StartOfTLine and EndOFTLine:

 c-Space   BlockClear StartOfTLine BlockContinuous EndOfTLine BlockContinuous
           Replace(" ", "", Block, NoLine, NoCase)

This is rather more verbose than the first example but it is important to note that BlockContinuous is a very useful function when creating a scope dynamically and we'll see more of it later.

After this initial, crude, example let's try something a bit more refined. A request that once came in was about removing superfluous spaces between words. Of course a Search&Replace is logical but again we need a scope to limit what it affects. Assuming the caret is between the two words concerned here's how it could be done (the _spcs expression used matches a sequence of spaces):

 c-Space   BlockClear Wordright() BlockMark_Continous() WordLeft()
           BlockMark_Continous() Replace(_spcs," ",Block) BlockClear()

An alternative is to use the MarkWord function (this relies on the _MarkWord expression matching a sequence of spaces as a word which the default does):

 c-Space   MarkWord() Replace(_spcs," ",Block) BlockClear()


KeywordFix, a StrongED utility soon to be released, corrects the use of case in keywords for a certain language. To be able to do this the word just entered must be turned into a selected block so that it can be passed to the Process function:

 c-Space   Push WordLeft MarkWord Pop SetTmp
           Process(Block,"Run <StrongED$Tmp_ToolPath>.KeywordFix") BlockClear

Search expressions too can be used to create a scope by combining the GotoNext and GotoPrev functions with BlockMark_Continous(). To select all text between two braces we could use this:

 c-Space   BlockClear GotoPrev("{",) BlockMark_Continous() GotoNext("}",)
           BlockMark_Continous() <functions to operate on the scope here>

There is one problem with the above in that it doesn't take nested braces into account. A solution is to use the GotoBracket function:

 c-Space   BlockClear GotoPrev("{",) CaretRight BlockMark_Continous()
           GotoBracket BlockMark_Continous() <further functions to go here>

A function found in other editors, such as TextMate, is the ability to jump to the first non-space character on a line. Here too dynamic scoping helps to bring this functionality to StrongED:

 c-Space   StartOfTline MarkWord GotoBlock_End CaretRight BlockClear

Finally we'll cover a scope that is very natural when working with text: a paragraph. As this is a more tricky scope we're going to have to define a search expression first:

 Paragraph   < ~\n {~(\n\n) (.|\n)}+ \n
 c-Space   BlockClear Push
           GotoNext(Paragraph,Text.NoLine,NoCase) BlockMark_Continous() Push
           GotoPrev(Paragraph,Text.NoLine,NoCase) BlockMark_Continous() Pop
           <functions to operate on the scope here>

The construction of the above is such that you can keep on pressing c-Space to treat each paragraph in turn, provided the additional functions don't alter the position of the caret.

Hopefully the above will have given you some idea of how to create scopes on the fly and apply functions to that scope.

Personal tools