Version 10, last updated by arst at July 15, 2008 03:04 UTC
The NSIS Output Parser
An NSIS Output Parser
The NsisMode class defined here page is complete in itself. But, if we want to have clickable error messages, we need to create another MiniAppMode extension, a command line output parser.
FWB is prepared for this scenario with a Squirrel class SqCmdPromptExt that can be extended for this purpose.
The SqCmdPromptExt Class
A command prompt object (of type maCommandPrompt) has an associated extension class maCmdPromptExtI. It contains methods that are triggered on various common command prompt actions:
| OnEnterCommand( macp, cmd, remains ) | The method is called when the user hits the enter key on a command line. It allows for preprocessing (or cancelling) the command line before sent to the prompt object. |
| OnOutput( macp, out_type, str, pos ) | This method is called with response lines generated by the underlying prompt object. The output can be parsed and its text colorized. |
| OnComplete( macp, line, ord ) | This method is called whenever the user requests completion of an input line. |
| OnMouse( macp, pos, action ) | This method is called when some mouse event (click, hover) happens in the prompt area. |
| … and many more | try list !SqCmdPromptExt in a SqSession prompt for a list of members. |
| … | or look in the source file fwb/maCmdPrompt.h for this interface. |
An instance of maCmdPrompt maintains a list of such extensions which are called in turn on these events.
SqCmdPromptExt is the Squirrel class corresponding to maCmdPromptExtI.
Implementing NsisPromptExt
NsisPromptExt implements SqCmdPromptExt for the NSIS compiler. It’s job is very simple:
- to locate error messages in compiler output,
- to colorize them,
- and respond when the errors are clicked.
We only have to override a few of the members to accomplish that.
In the same script file NsisMode.nut we add our new class definition:
@ class NsisPromptExt extends SqCmdPromptExt {@ @ @ @ function constructor(){@ @ ::SqCmdPromptExt.constructor()@ @ // If we’re created, write a global ref to us@ @ ::__nsis_prompt_ext <- this@ @ }@ @ @ @ function GetTypeName( ){@ @ return “Nsis Output Parser”;@ @ }@We call the base class constructor so that we are fully constructed and registered with the MiniAppManager. Then, we write this new instance into the global variable __nsis_prompt_ext (then an existing instance can be detected).
The AcceptApp Function
@ function AcceptApp( apptype, app ){@ @ if( apptype.GetTypeName()!=“Cmd Prompt” ) return 0@ @ if( !app )@ @ return 1@ @ return AcceptOsPrompt(app) ? 1 : 0@ @ }@When app is null, it’s a question if we’re interested in this type in general. We are, if the type is “Cmd Prompt”.
If app is not null, we must look at app itself and determine if it is an OS shell command prompt or not. The function AcceptOsPrompt(app) does just that.
The OnOutput Function
Next is the output parsing function. It is called whenever a single full output line is ready from the prompt object. In output like:
@ OutFile: “tools.exe”@ @ Invalid command: bbGGradient@ @ Error in script “tools.nsi” on line 6 — aborting creation process@we want to find lines like the third one and hilight it as an error. The PCRE regular expression:
@ static ms_re_nsis_err = PCRE\\\" on line (\\d*)") @
matches such lines (and puts the file name in the first capture and the line number in the second one). So we get:
@ function OnOutput( macp, out_type, line, pos ){@ @ if( ms_re_nsis_err.Match(line)>1 ){@ @ // Hilight this line – up to line number@ @ local hilight_len = ms_re_nsis_err.SubStringPos(2) + ms_re_nsis_err.SubStringLength(2)@ @ macp.SetOutputStyle( pos, hilight_len, ms_scint_style_link_error )@ @ }@ @ return 0@ @ }@The PCRE functions SubStringPos(nr) and SubStringLength(nr) returns the position and length of a regular expression capture. With that information, we can call SetOutputStyle(…) for the error matching string. We hilight it to error color (_ms_scint_style_link_error).
The OnMouse Function
This function is called whenever a mouse button is clicked in the window, or when the mouse hovers at one point.
We want to pick out left clicks on our hilighted errors:
@ function OnMouse( macp, pos, type ){@ @ //println( "NsisPromptExt::OnMouse – " + type + " pos-" + pos )@ @ if( type==ms_LeftClick ){@Next we want to check if a highlighted error was clicked. The TextEditor interface contains functions that help us here:
@ local te = macp.GetObj(“TextEditor”)@ @ local style = te.GetStyleAt(pos)@ @ if( style==ms_scint_style_link_error ){@Each character in the Scintilla text editor has style bits stored with it, and we access that through GetStyleAt(pos).
@ local msg = GetStyledStringAt(te,pos)@ @ if( ms_re_nsis_err.Match(msg)<1 ) {@ @ println( "NsisPromptExt – not ours: "+msg )@ @ return 0@ @ }@The function GetStyledStringAt(te, pos) is a base class member and gives us a full string where all characters have the same style (extending left and right from pos). That string must match our error regular expression, otherwise the error does not belong to us. Returning 0 here allows other parsers to process the output.
@ // Capture 1 is filename and capture 2 is line number @ @ local fname = ms_re_nsis_err1@ @ local lnr = ms_re_nsis_err2.tointeger()@We can extract filename and line number from regular expression captures. Next we want to locate the file where the error happened:
@ // Give to editor and set line @ @ if( IsPathRelative(fname) ){@ @ local dir = macp.GetDirAt(pos)@ @ fname = vtree.LocateFile( dir, "", fname, 3 )@ @ } @Here we use a maCmdPrompt function which returns the current directory at a specific position in the command prompt window. (Since commands can change the working directory, FWB keeps track of the directories at various points in the history. Otherwise we would not be able to resolve output links when clicking old commands.)
vtree.LocateFile(…) is used to find the full path of the named file. Next we need to find an editor and point it at the error line.
@ local editor = ::fwb_edit(fname)@ @ if( editor && (editor=editor.GetObj(“TextEditor”)) )@ @ editor.GotoLine( lnr-1 )@ @ @ @ return 1@We use the function fwb_edit(path) which does the job of locating an editor for us. Then, we tell the editor to go to the right line. Finally we return 1 to indicate that we successfully parsed the output.
@ }@ @ }@ @ return 0@ @ }@That’s all. Writing an output parser in Squirrel proved a simple thing.
h2. Other Pages
Back to The NsisMode MiniAppMode.
Back to NSIS Text Editor Mode
Back to Scripting FWB