[code] Textredux for WIN32: Using Findstr.exe

From: Gavin Holt <holtgavin.att.gmail.com>
Date: Mon, 27 Jan 2020 17:28:39 +0000

Hi,

Standing upon the shoulders of giants - I have modified this find in file
command[1] to use findstr.exe[2]. Findstr is a system file in most versions
of windows. It is quite powerful, but does have some quirks[3]. I also use
a utility function[4] to extract just the filenames - leaving space for the
text with the full filepath in column 4.

    function io.filename(filename)
    -- Source:
http://stackoverflow.com/questions/5243179/what-is-the-neatest-way-to-split-out-a-path-name-into-its-components-in-lua
        if filename then
            local mypath, myfile, myext = string.match(filename,
"(.-)([^\\]-([^%.]+))$")
            return myfile
        else
            return nil
        end
    end

    function func.fif()
    -- Source:
https://github.com/loomer/.textadept/tree/master/modules/tr_fif
        local function fif_cb(path, exists, list)
            list:close()

            local retval, input = ui.dialogs.inputbox{
              title = 'Find in Files',
              informative_text = 'Findstr.exe /npi '}
            if retval == -1 then
              return
            end

            local cmd = "findstr.exe /npi " .. input .. " " .. path .."*.*"
            local file = assert(io.popen(cmd, 'r'))
            local result = assert(file:read('*a'))
            file:close()

            results_table = {}
            for line in result:gmatch("[^\r\n]+") do
              local file, num, code = line:match("^(.+):(%d+):(.+)$")
              if file then
                results_table[#results_table + 1] = {io.filename(file),
num, code, file}
              else
                results_table[#results_table + 1] = {line}
              end
            end

            if #results_table == 0 then
              ui.statusbar_text = "[FiF]: No results for: " .. input
              return
            end

            -- create result list
            local results_list = textredux.core.list.new('Find in Files')
            results_list.column_styles = {
                textredux.core.style.string,
                textredux.core.style.number,
                textredux.core.style.default}
            results_list.headers = {
                'File',
                'Line',
                'Text'}
            results_list.items = results_table
            results_list.on_selection = function(list, item)
              io.open_file(item[4])
              if tonumber(item[2]) then
                buffer:goto_line(item[2] - 1)
                buffer:vertical_centre_caret()
              end
            end
            results_list:show()
          end

      textredux.fs.select_directory(fif_cb)
    end

The fif function first asks for a directory to search (right arrow to
select) and then offers a command line. My script uses "findstr.exe /npi ??
*.* SelectedDir". The /n prints a line number, the /p skips binary files
and the /i make the search case insensitive.

To search down into sub-directories prefix your search term with /s. Spaces
separate terms, which implies "OR". The default is to treat each term as a
regular expression. To search for phrases use the /c switch (literal
search) and send a parameter in quotes e.g. /c:"hello there". To specify a
file glob add it after the search terms - default is "*.*".

Extending the use of this utility I have two other examples:

1. Search just a list of the open files and present matching lines in order.

    function func.buffer_filenames()
        local buffers = {}
        for i,v in pairs(_BUFFERS) do
            if type(v)=="table" and v.filename then
                buffers[v.filename] = 1
            end
        end
        local buffer_filenames = {}
        for i,v in pairs(buffers) do
            table.insert(buffer_filenames,i)
        end
        return buffer_filenames
    end

    function func.findinOpen()
    -- List all occurrences in all open files - note this searches the disk
copy
        -- Source:
https://github.com/loomer/.textadept/tree/master/modules/tr_fif
        -- Check for filename
        if not io.exists(buffer.filename) then return end

        -- Create a list of buffers
        local buffers = table.concat(func.buffer_filenames()," ")

        -- Load the file contents
        local cmd = "findstr.exe /npi /R . ".. buffers .." .. "
        local file = assert(io.popen(cmd, 'r'))
        local result = assert(file:read('*a'))
        file:close()

        -- Create results list
        results_table = {}
        for line in result:gmatch("[^\r\n]+") do
           local file, num, code = line:match("^(.+):(%d+):(.+)$")
           if file then
                results_table[#results_table + 1] = {io.filename(file),
num, code, file}
           else
                results_table[#results_table + 1] = {line}
           end
        end

        -- Display result list
        local results_list = textredux.core.list.new('Find in editor')
        results_list.search_fuzzy = false
        -- This also stops line sorting due to:
        -- TextAdept\conf\modules\textredux\util\matcher.lua:114 -
Modification
        results_list.column_styles = {
            textredux.core.style.string,
            textredux.core.style.number,
            textredux.core.style.default}
        results_list.headers = {
            'File',
            'Line',
            'Text'}
        results_list.items = results_table
        results_list.on_selection = function(list, item)
          io.open_file(item[4])
          if tonumber(item[2]) then
            buffer:goto_line(item[2] - 1)
            buffer:vertical_centre_caret()
          end
        end
        results_list:show()

    end

For this function I have switched off "sort by relevance" in
modules\textredux\util\matcher.lua:114. In fact I have modified the match
function so that any call without "search_fuzzy" will not be sorted:

      if self.search_fuzzy then
        table.sort(matches, function(a ,b) return a.score < b.score end)
      end

NB. I'm sure this function could be made better by searching the live
buffer contents, which may include unsaved edits. I may try this next time
I have a long weekend.

2. Find all my TODO: labels ;)

function func.findTODO()
-- List all occurrences recusing down directory branch
    -- Load the file contents
    local cmd = "findstr.exe /nps TODO: ".. "*.*" .." .. "
    local file = assert(io.popen(cmd, 'r'))
    local result = assert(file:read('*a'))
    file:close()
    -- Display
    results_table = {}
    for line in result:gmatch("[^\r\n]+") do
        if not line:match([[zBackup]]) then -- Remove my primitive backup
files
            local file, num, code = line:match("^(.+):(%d+):(.+)$")
            if file then
                -- Shorten filename by removing top path
                results_table[#results_table + 1] = {io.filename(file),
num, code, file}
            else
                results_table[#results_table + 1] = {line}
            end
        end
    end
    -- create result list
    local results_list = textredux.core.list.new('Find TODO in branch')
    results_list.search_fuzzy = true
    results_list.column_styles = {
        textredux.core.style.string,
        textredux.core.style.number,
        textredux.core.style.default}
    results_list.headers = {
        'File',
        'Line',
        'Text'}
    results_list.items = results_table
    results_list.on_selection = function(list, item)
      io.open_file(item[4])
      if tonumber(item[2]) then
        buffer:goto_line(item[2] - 1)
        buffer:vertical_centre_caret()
      end
    end
    results_list:show()
end

In the future it would be really nice to apply syntax highlighting to the
text column - correct for the file type where the line came from! Any help
appreciated.

Kind Regards Gavin Holt

[1] https://github.com/loomer/.textadept/tree/master/modules/tr_fif
[2]
https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/findstr
[3]
https://stackoverflow.com/questions/8844868/what-are-the-undocumented-features-and-limitations-of-the-windows-findstr-comman#8844873
[4]
http://stackoverflow.com/questions/5243179/what-is-the-neatest-way-to-split-out-a-path-name-into-its-components-in-lua

-- 
You are subscribed to code.att.foicica.com.
To change subscription settings, send an e-mail to code+help.att.foicica.com.
To unsubscribe, send an e-mail to code+unsubscribe.att.foicica.com.
Received on Mon 27 Jan 2020 - 12:28:39 EST

This archive was generated by hypermail 2.2.0 : Tue 28 Jan 2020 - 06:53:30 EST