4.6.1. Applying external tool to selected text

Let's suppose you want to apply some AWK program to your selected text in Notepad++.

The first thing you require is NppExec plugin ;)

The second thing you require is an AWK executable, for example, "awk95.exe" or "gawk.exe".

When you want to apply some AWK program to your selected text, this AWK program must exist, isn't it? So, create a file "do.awk" in your Notepad++'s folder. The full path to this file is "$(NPP_DIRECTORY)\do.awk".

OK, the file is created, now you need some AWK program. Open the "do.awk" in Notepad++ and type your program, for example:

{ print $0 }

This simple AWK program will print each record (i.e. text line) from the input file given.

Now, open another file in Notepad++ and select some text (few lines, for example).

Now you have the selected text and a tool (awk95.exe or gawk.exe) you want to apply to this selected text.

The only thing remaining is NppExec's script which will allow you to do what you want.

Press F6 (or Plugins -> NppExec -> Execute NppExec Script...) and type:

// full path to AWK executable 
set local AWK_EXE = C:\tools\awk\awk95.exe 
// this temporary file name will be used 
set local TEMP_FILE = $(SYS.TEMP)\npp_sel.txt 
// save current selection as ANSI text file 
SEL_SAVETO $(TEMP_FILE) :a 
// run do.awk for this file 
"$(AWK_EXE)" -f "$(NPP_DIRECTORY)\do.awk" "$(TEMP_FILE)"

Save this script as "do.awk on selected text" or whatever you want.

Press OK.

You will see the AWK's output in the NppExec's Console window. You can copy it from there with Ctrl+C.

As you understand, such approach can be used for any command-line external tool such as grep, php and so on.

Good luck!

 

P.S. By the way, if you have nothing selected and you want to select a word under the caret, the following script can be used:

// get current position...
SCI_SENDMSG SCI_GETCURRENTPOS
set local pos = $(MSG_RESULT)
// get start of a word near the pos...
SCI_SENDMSG SCI_WORDSTARTPOSITION $(pos) 1
set local wordStart = $(MSG_RESULT)
// get end of a word near the pos...
SCI_SENDMSG SCI_WORDENDPOSITION $(pos) 1
set local wordEnd = $(MSG_RESULT)
// set selection
SCI_SENDMSG SCI_SETSEL $(wordStart) $(wordEnd)
 

If you combine this script with the previous one, you will be able to apply an external tool to a word under the caret. (I.e. first it will select a word under the caret, and then will apply an external tool to that selected word.) Also consider ability to use $(CURRENT_WORD) instead of SEL_SAVETO in this case (see [4.6.3]).

 

P.P.S. If you don't want to use a temporary file and pass the selected text via a command line instead, refer to the following approach.
We'll be sending the selected string to an external tool by means of cmd /C echo, so let's create an auxiliary NppExec's script that will escape special characters for the echo command:

// escape_string_for_cmd_echo
if $(ARGC) != 2 then
  set ECE_RESULT = 0  // failed
  exit
endif
set local s ~ strreplace `$(ARGV[1])` `^` `^^^^`  // double-escaping the '^' char
set local s ~ strreplace `$(s)` `|` `^^^|`  // double-escaping the '|' char
set local s ~ strreplace `$(s)` `&` `^^^&`  // double-escaping the '&' char
set local s ~ strreplace `$(s)` `<` `^^^<`  // double-escaping the '<' char
set local s ~ strreplace `$(s)` `>` `^^^>`  // double-escaping the '>' char
set local s ~ strreplace `$(s)` `(` `^(`  // escaping the '(' char
set local s ~ strreplace `$(s)` `)` `^)`  // escaping the ')' char
set local s ~ strreplace `$(s)` `"` `^"`  // escaping the '"' char
set ECE_STR = $(s)  // the escaped string
set ECE_RESULT = 1  // succeeded

Save this script as "escape_string_for_cmd_echo".
Now, having the auxiliary script above, let's create an NppExec's script that will send the selected string to an external tool:

npp_console local -  // disabling output to the Console
npe_console local -- m-  // disabling the internal messages
set local AWK_EXE = C:\tools\awk\awk95.exe  // path to an external tool
npp_exec escape_string_for_cmd_echo `$(SELECTED_TEXT)`
if $(ECE_RESULT) != 1 then
  npp_console local +  // enabling the output
  echo ERROR: Could not escape a string (maybe it contains the ` symbol?)
  exit
endif
set local s = $(ECE_STR)  // the escaped string
npp_console local +  // enabling the output
cmd /C echo $(s)| "$(AWK_EXE)" -f "$(NPP_DIRECTORY)\do.awk"

This example works with a single-line selection. But what if the selection is multi-line? In such case, we may need to split the selected text to lines and pass each line to an external tool. The corresponding NppExec's script will be noticeably more complicated. Here it is:

npp_console local -  // disabling output to the Console
npe_console local -- m- n-  // no internal messages, no empty lines condensing
set local AWK_EXE = C:\tools\awk\awk95.exe  // path to an external tool
npp_exec escape_string_for_cmd_echo `$(SELECTED_TEXT)`
if $(ECE_RESULT) != 1 then
  npp_console local +  // enabling the output
  echo ERROR: Could not escape a string (maybe it contains the ` symbol?)
  exit
endif
set local s = $(ECE_STR)  // the escaped string
set local LF ~ chr 0x0A  // '\n' character
set local CR ~ chr 0x0D  // '\r' character
:Loop
// note: we use `` to preserve leading and trailing spaces in `$(s)`
set local posLF ~ strfind `$(s)` `$(LF)`  // position of LF
set local posCR ~ strfind `$(s)` `$(CR)`  // position of CR
set local pos = $(posCR)
if $(pos) == -1 then
  set local pos = $(posLF)  // no CR
else if $(pos) > $(posLF) then
  set local pos = $(posLF)  // LF is before CR
endif
if $(pos) != -1 then
  set local line ~ substr 1 $(pos) `$(s)`  // part of the string before CR or LF
else
  set local line ~ substr 1 -1 `$(s)`  // the remaining string
endif
set local len ~ strlen `$(line)`  // note: `` preserves leading/trailing spaces
set local len ~ $(len) - 2  // length without ``
if $(len) != 0 then
  set local cmd1 = cmd /C echo $(line)
else
  set local cmd1 = cmd /C echo.
endif
npp_console local +  // enabling the output
$(cmd1)| "$(AWK_EXE)" -f "$(NPP_DIRECTORY)\do.awk"
npp_console local -  // disabling the output
if $(pos) != -1 then
  // there is CR or LF remaining
  set local pos1 ~ $(pos) + 1  // position considering '`' in `$(s)`
  set local c ~ substr $(pos1) 1 `$(s)`  // either CR or LF char
  set local pos ~ $(pos) + 1  // skip this character
  if "$(c)" == "$(CR)" then
    set local pos1 ~ $(pos) + 1  // position considering '`' in `$(s)`
    set local c ~ substr $(pos1) 1 `$(s)`  // check the next char after CR
    if "$(c)" == "$(LF)" then
      set local pos ~ $(pos) + 1  // skip LF after CR
    endif
  endif
  set local pos1 ~ $(pos) + 1  // position considering '`' in `$(s)`
  set local s ~ substr $(pos1) -1 `$(s)`  // the remaining part of the string
  goto Loop
endif

This last script sends each line of the selected text to a new instance of cmd /C echo. This can be time-consuming for a lot of lines. Instead, if the external tool supports string splitting, we may alter our approach. In particular, we may do the following steps:

  1. replace all the EOL (end-of-line) characters in the selected text with a unique sequence of characters (such as "~@#=");
  2. pass the resulted string to the external tool;
  3. the external tool will split its received string using the same unique sequence of characters as the separator (e.g. "~@#=");
  4. finally, the external tool will process the lines that have been split.

In this case, the AWK program "do.awk" will become:

{
  sep = "~@#="
  n = split($0, a, sep)
  for (i = 1; i <= n; i++)
  {
    print a[i]  # print each line
  }
}

And here is the corresponding NppExec's script:

npp_console local -  // disabling output to the Console
npe_console local -- m- n-  // no internal msgs, no empty lines condensing
set local LF ~ chr 0x0A  // '\n' character
set local CR ~ chr 0x0D  // '\r' character
sci_sendmsg SCI_GETEOLMODE  // get EOL from Scintilla
set local eol = $(MSG_RESULT)
if $(eol) == 2 then
  set local eol = $(LF)  // "\n"
else if $(eol) == 1 then
  set local eol = $(CR)  // "\r"
else
  set local eol = $(CR)$(LF)  // "\r\n"
endif
set local alt_eol = ~@#=  // unique combination of characters
set local s ~ strreplace `$(SELECTED_TEXT)` `$(eol)` `$(alt_eol)`
if $(LAST_CMD_RESULT) != 1 then
  npp_console local +  // enabling the output
  echo ERROR: the selected text contains the ` symbol
  exit
endif
npp_exec escape_string_for_cmd_echo `$(s)`
if $(ECE_RESULT) != 1 then
  npp_console local +  // enabling the output
  echo ERROR: Could not escape a string (maybe it contains the ` symbol?)
  exit
endif
set local s = $(ECE_STR)  // the escaped string
set local t ~ strstrip 0x23 `$(s)`
if `$(t)` != `` then
  set local cmd1 = cmd /C echo $(s)
else
  set local cmd1 = cmd /C echo.
endif
npp_console local +  // enabling the output
$(cmd1)| awk -f "$(NPP_DIRECTORY)\do.awk"

Note: all the three last NppExec's scripts use the "escape_string_for_cmd_echo" script mentioned above. So be sure it has been created and saved.

 

P.P.P.S. If you want to deal with the entire text of the document rather than with the selected text, the following script can be used:

sci_sendmsg SCI_GETTEXTLENGTH  // retrieving the text length into $(MSG_RESULT)
set local textLen ~ $(MSG_RESULT)  // the text length
set local bufSize ~ $(textLen) + 1  // the text length plus the trailing '\0'
npe_sendmsgbuflen local $(bufSize)  // ensuring the $(bufSize) is allocated for SCI_GETTEXT
sci_sendmsg SCI_GETTEXT $(textLen) @""  // retrieving the text into $(MSG_LPARAM)
echo $(MSG_LPARAM)  // doing something with the retrieved text - e.g. printing
 

See also: Processing & replacing the selection [4.6.3]; Modify selected text and save to file [4.6.17].