Sublime Forum

Fold Away Lines Not Matching A Pattern

#1

Back in my IBM 3270 mainframe days we used an editor called XEdit. It had a neat feature whereby you could hide away all lines in a file not matching a pattern or keyword.

There is something similar for Vim, called foldsearch (http://www.vim.org/scripts/script.php?script_id=2302).

I suspect some smart person could write a Sublime plugin to do this. Maybe one exists and I haven’t found it yet.

0 Likes

Help needed with a simple filter plugin
#2

I did this for my ADB logcat plugin. It shouldn’t be too hard for someone to adapt it to a separate plugin. Cutting and pasting the relevant code bit:

view.run_command("unfold_all") endline, endcol = view.rowcol(view.size()) line = 0 currRegion = None regions = ] while line < endline: region = view.full_line(view.text_point(line, 0)) data = view.substr(region) if self.filter.search(data) == None: if currRegion == None: currRegion = region else: currRegion = currRegion.cover(region) else: if currRegion: regions.append(currRegion) currRegion = None line += 1 if currRegion: regions.append(currRegion) view.fold(regions)

0 Likes

#3

[quote=“quarnster”]I did this for my ADB logcat plugin. It shouldn’t be too hard for someone to adapt it to a separate plugin. Cutting and pasting the relevant code bit:

view.run_command("unfold_all") endline, endcol = view.rowcol(view.size()) line = 0 currRegion = None regions = ] while line < endline: region = view.full_line(view.text_point(line, 0)) data = view.substr(region) if self.filter.search(data) == None: if currRegion == None: currRegion = region else: currRegion = currRegion.cover(region) else: if currRegion: regions.append(currRegion) currRegion = None line += 1 if currRegion: regions.append(currRegion) view.fold(regions) [/quote]

Outstanding. I cobbled this together despite my horrible Python skills. I called it All because that’s what it was called back on the mainframe.

import sublime, sublime_plugin

class AllCommand(sublime_plugin.TextCommand):

 def done(self,arg):
   self.foldstr = arg
   self.all()

 def change(self,arg):
   pass
   
 def cancel(self):
   pass

 def all(self):
    view = self.view
    view.run_command("unfold_all")
    endline, endcol = view.rowcol(view.size())
    line = 0
    currRegion = None
    regions = ]

    while line < endline:
       region = view.full_line(view.text_point(line, 0))
       data = view.substr(region)
       if data.find(self.foldstr) == -1:
            if currRegion == None:
               currRegion = region
            else:
               currRegion = currRegion.cover(region)
       else:
            if currRegion:
               regions.append(currRegion)
               currRegion = None
       line += 1
       if currRegion:
         regions.append(currRegion)
       view.fold(regions)
 
 def run(self, edit):
    window = self.view.window()
    window.show_input_panel("Fold Away Except","",self.done,self.change,self.cancel) 

Thanks for your help.

0 Likes

#4

If anyone is playing along I made this work MUCH faster.

[code]import sublime, sublime_plugin

class AllCommand(sublime_plugin.TextCommand):

def done(self,arg):
self.foldstr = arg
self.all()

def all(self):
view = self.view
view.run_command(“unfold_all”)
endline, endcol = view.rowcol(view.size())
line = 0
firstRegion = None
currRegion = None
regions = ]

while line < endline:
   region = view.full_line(view.text_point(line, 0))
   data = view.substr(region)
   if data.find(self.foldstr) == -1:
        if firstRegion == None:
           firstRegion = region
           lastRegion  = region
        else:
           lastRegion  = region
   else:
        if firstRegion:
           currRegion = firstRegion.cover(lastRegion)
           regions.append(currRegion)
           firstRegion = None
   line += 1
   if currRegion:
     regions.append(currRegion)
     currRegion = None

if firstRegion:
  currRegion = firstRegion.cover(lastRegion)
  regions.append(currRegion)
  firstRegion = None         
view.fold(regions)

def run(self, edit):
window = self.view.window()
for reg in self.view.sel():
defstr = self.view.substr(reg)
break
window.show_input_panel(“Show Only Lines Containing”,defstr,self.done,None,None) [/code]

0 Likes

#5

Maybe someone can explain to me how to use this plugin?

I have placed the plugin into a file called “all.py” in the Packages/User directory.

Then I call view.run_command(“all”) from the console, and nothing happens.

So how does one make use of this plugin?

[code]import sublime, sublime_plugin

class AllCommand(sublime_plugin.TextCommand):

def done(self,arg):
   self.foldstr = arg
   self.all()

def all(self):
    view = self.view
    view.run_command("unfold_all")
    endline, endcol = view.rowcol(view.size())
    line = 0
    firstRegion = None
    currRegion = None
    regions = ]

    while line < endline:
       region = view.full_line(view.text_point(line, 0))
       data = view.substr(region)
       if data.find(self.foldstr) == -1:
            if firstRegion == None:
                firstRegion = region
                lastRegion  = region
            else:
                lastRegion  = region
       else:
            if firstRegion:
                currRegion = firstRegion.cover(lastRegion)
                regions.append(currRegion)
                firstRegion = None
       line += 1
       if currRegion:
          regions.append(currRegion)
          currRegion = None

    if firstRegion:
        currRegion = firstRegion.cover(lastRegion)
        regions.append(currRegion)
        firstRegion = None
    view.fold(regions)

def run(self, edit):
window = self.view.window()
for reg in self.view.sel():
defstr = self.view.substr(reg)
break
window.show_input_panel(“Show Only Lines Containing”,defstr,self.done,None,None)[/code]

0 Likes

#6

you should trigger this command in context of a view.
either add keybinding in your User/Default (OS).sublime-keymap

{ "keys": "ctrl+alt+"], "command": "all" }

to trigger thourgh command menu add this to User/Default.sublime-commands

{
  "caption": "Fold All Not Matching",
  "command": "all"
}
0 Likes

#7

[quote=“vitaLee”]you should trigger this command in context of a view.
either add keybinding in your User/Default (OS).sublime-keymap

{ "keys": "ctrl+alt+"], "command": "all" } [/quote]

I tried this and ctrl+alt+" does nothing.

Doesn’t this do the exact same thing as doing view.run_command("all") from the console anyway though?

0 Likes

#8

you’re right, that should do the job too.
any errors for you in the console or in dialogs ?
do you have all.pyc file next to all.py in Packages/User ?

0 Likes

#9

No :frowning:

Yes.

0 Likes

#10

Thanks this works great. This was the one thing that I really needed this editor to do. Great for working with log files.

Only had to fix the indenting. Thanks so much!!

Edit: Here is my contribution. the “all_toggle” command. You can bind it, and once you perform your all command you can easily toggle back and forth between showing all lines and showing the result of all

[code]import sublime, sublime_plugin

class AllCommand(sublime_plugin.TextCommand):

def done(self,arg):
   self.foldstr = arg	   
   self.all()

def all(self):
    view = self.view
    view.run_command("unfold_all")
    endline, endcol = view.rowcol(view.size())
    line = 0
    firstRegion = None
    currRegion = None
    regions = ]

    while line < endline:
       region = view.full_line(view.text_point(line, 0))
       data = view.substr(region)
       if data.find(self.foldstr) == -1:
            if firstRegion == None:
               firstRegion = region
               lastRegion  = region
            else:
               lastRegion  = region
       else:
            if firstRegion:
               currRegion = firstRegion.cover(lastRegion)
               regions.append(currRegion)
               firstRegion = None
       line += 1
       if currRegion:
         regions.append(currRegion)
         currRegion = None

    if firstRegion:
      currRegion = firstRegion.cover(lastRegion)
      regions.append(currRegion)
      firstRegion = None         
    view.fold(regions)
    view.settings().set("all", self.foldstr)

def run(self, edit):
    if self.view.settings().has("all"):
    	self.done(self.view.settings().get("all"))
    	return
    window = self.view.window()
    for reg in self.view.sel():
      defstr = self.view.substr(reg)
      break
    window.show_input_panel("Show Only Lines Containing",defstr,self.done,None,None)

class AllToggleCommand(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
if view.settings().has(“all”):
if len(view.folded_regions()):
view.run_command(“unfold_all”)
else:
view.run_command(“all”) [/code]

0 Likes

#11

Forgive me as I am not a coder, but how can you adapt the code so that it folds away lines that DO match the pattern? And/or to allow regex search?

Also, sylvanaar’s suggestion is a good one (as otherwise you have to do something like press ctrl-a, then ctrl-shift-] to unfold), but two problems:

  1. After running the command on a particular file, when command rerun it folds for the same text as before and the selection box no longer appears.
  2. I bound a “all_toggle” command but it does nothing.
0 Likes

#12

I’ve tinkered with this to improve its behavior (for what I’m using it for). I’m not a programmer, so it’s rather rickety.

You can find my version here:
raw.github.com/alehandrof/MDSH/ … %20Fold.py

Bind a key as follows

{ "keys": "ctrl+'"], "command": "all_toggle", "args": { "foldstr": ""  } },

This command will toggle between:

  • unfold all lines, and
  • fold all lines EXCEPT those matching “foldstr”

“Foldstr” can be passed as an argument, by selected text, or input.

What I use this for is to parse a todo list based on #tags and @contexts:
find a place in the text, hit Ctrl+D to select the word, and Ctrl+’ to fold all lines except those matching the word.

Hope this makes sense / is useful.

Alex

0 Likes

#13

Cheers Alex, I did manage to figure out that commenting out:

# if self.view.settings().has("all"):
#   self.done(self.view.settings().get("all"))
#   return

stopped the repeating of the filter. And having a single command to switch between folding state seems a sensible behaviour. I had a similar usage scenario to yours, though I also wanted to allow filtering out of lines (e.g. those with a @done tag).

0 Likes

#14

The argument is also quite handy, because it means you can set up commands in the palette to fold for standard tags like @home, @calls, etc. (I also list agenda items by @person, as in @bob.)

I’ve been thinking of adding an exclusion filter for the same reason (the files I’m using are loosely based on todo.txt so I would be looking to exclude a regex like ^x \d{4}-\d{2}-\d{2}). It shouldn’t be too tricky, but I don’t entirely understand the code of the all function. I’ll take a stab at it when I can. Let me know if you come up with anything.

Alex

0 Likes

#15

I did find this:
superuser.com/questions/452189/h … ime-text-2
And tried to adapt it, but my understanding of the coding is limited. But having a separate regular text search and regex search would be good.

This regex filter in the link would be useful for destructive removal of items (e.g. @done lines)

Use of regex with negative look arounds can also be used to filter out instances, so this:
^((?!DONE).)*$
Will match any string, or line without a line break, not containing the (sub) string ‘DONE’. You could then combine that with regex term to exclude dones and include something else, which means the regex would start getting complicated, but running from the palette would help.

0 Likes

#16

That’s a nice find. Although I suspect the folding, rather than the regex, is the tricky bit.

Actually, I don’t really like the folding on this plugin, I would prefer the fold market at the end. You mention SmartMarkdown elsewhere, which has a similar problem: the folding marker is underneath the # heading, which makes it more likely I will leave something behind when moving chunks around. I would prefer if it folded like so:

# heading ...]
## other heading ...]
&c.

Incidentally, did you notice that all your posts are in threads I had started or posted in? – with this exception:

(I’m surprised that didn’t take off, @facelessuser and @castles_made_of_sand can usually pull amazing things seemingly out of a hat.)

Anyway, if you tell me what you’re trying to cook up, I can tell you whether I have failed to find it/make it :smiley:

Alex

0 Likes

#17

I guess, without really being aware of its existence, that I have inadvertently being trying to emulate foldingtext which I recently discovered, which is a freeform outliner/task manager from the maker of taskpaper (of which plaintasks is a copy of).

If you watch the folding text video they use folds next to the item rather than below, as you were suggesting.
foldingtext.com/about/

To avoid overly complicated and lengthy nested structures, markdown headings (with folding) allow structure, but still provide nesting if you want with tabbed bullet points below them (which can also be folded with native sublime), and it all looks nice in markdown. One minor issue with markdone syntax is that the state of item (e.g. + for completed) isn’t shown in markdown view, which is why appending @done to items is useful. There is the itodo plugin which works well with markdone to make converting todo items ( starting with -) to done items, e.g.:

  • done task @done (2012-11-16 16:59)

One thing that I haven’t seen in sublime is a “zoom” mode, which is what workflowy does well, and is found in foldingtext and writemonkey - so you press a hotkey and you only see the current scope (e.g. the level of the heading within markdown), and “zoom out” to see the whole document.

Anyway, putting all these things together makes for something pretty similar to foldingtext and potentially quite a nice and powerful tool. If I was more technically minded I would think about creating a package.

0 Likes

#18

It sounds like we’re treading pretty similar territory.

After about a year of using half-baked workflows I had cobbled together from orgmode, todotxt, iTodo, and various other packages, I decided to switch to Plain Tasks a few weeks ago. Almost immediately I started re-writing it to make it more Markdown-like. After a few days I gave up and went back to my half-baked system, which currently consists of the following:

  • A syntax almost identical to todo.txt, along with some useful commands to add and complete actions. This is where I find use for the All Fold command, because the syntax is just one item per line. I’ve taken the plunge and am trying to implement a full GTD system, and, although I dislike this format, I found I have the least resistance to using it.

  • A syntax which is a sort of fast-and-loose Markdown. For example, you can cram things next to each other without empty lines (which is not valid in strict Markdown):

# Heading 1
## Heading 2
- List item
## Heading 2

This syntax is in flux always and mostly broken at the moment.

I’ve gone ahead and installed the Clickable URLs plugin and chucked my own orgmode-derived abominations.

I have some mostly working code for opening local files with Wiki style links [other-file.md]. (From the Wiki plugin).

I use SmartMarkdown but it doesn’t “feel” right, although it’s probably an improvement over what Sublime provides by default. Navigating a file with lots of folded coded is very annoying and very easy to erase entire chunks. I wish there was a plugin that made folded text indestructible.

I think that “zoom” is an interesting idea. It should be possible to write a plugin that does this by folding the text above and below the current location. I think a sane way of doing this is to re-write the Markdown (or whatever) syntax so that there’s a scope defined between headings (including the heading itelf), otherwise you have to resort to regexs at the plugin level. (Does this make sense?)

I’m very fond of the orgmode checkboxes:

-  ] Incomplete item
- [X] Completed item

For about the first half of 2012, all my organization was basically Markdown + orgmode-style checkboxes.

(Then this happened: viewtopic.php?f=3&t=7388&hilit=+usb#p34327 )

I just watched the Folding Text video again, and I noticed that the “.todo” lists use checkboxes. I’ve been trying to figure out how to use checkboxes or checkbox-like things with the minimum of conversion difficulties (via Pandoc and the like) and have encountered the difficulty you also point out. I think I’ll go back to combining the Markdown syntax with the checkboxes (and maybe some bits of the GTD syntax/commands) and worry about how to convert to other formats later.

Although I like the Workflow/PaperTasks/FoldingText simplicity, one of the things I’ve found really useful is to date everything, so I can refer to it later. I only need the date, not the time, but it’s pretty intrusive:

- incomplete item @added(2012-02-06)
+ complete item @added(2012-05-24) @done(2012-06-02)

In my todo.txt syntax, I color it using the comment scope so that I can tune it out, but I can only do so because the dates are always at the beginning of the line.

Maybe folding these meta-tags, coupled with the orgmode checkboxes is the way to go:

-  ] incomplete item ...]
- [X] complete item ...]

I’m not quite sure how this would work, though.

(Sorry for the long, chaotic post.)

Alex

0 Likes

#19

I opened an issue on SmartMarkdown regarding folding the content on the same line as the heading:
github.com/demon386/SmartMarkdown/issues/12

(Although the developer of SmartMarkdown has stated he’s quite busy.)

0 Likes

#20

Does it support multiple pattern or single matching ?

0 Likes