Dynamic modification of text search results

  • Dear List,

    I asked this question in January but didn't get a reply. Since I haven't come up with any good/solid ideas myself, I thought I'd ask again, in case this time it triggers some thought.

    I have an app which managed a bunch of files and presents these text files for viewing and editing in NSTextViews. I have implemented a project-wide search functionality which works fine, but with one problem. If the user changes the text in one file after making a search, then the search results are no longer valid for that file. A typical use-case is to make a search for a word or phrase, then go through each result and modify it. Problem is, after the first modification by the user, the subsequent results for that file are no longer accurate/valid. A result constitutes the file that the match is in, and the range within the text.

    Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.

    Cheers,

    Martin
  • > Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.

    Couple of random suggestions:

    * If you're using NSAttributedString, you can mark your search results by assigning custom attributes to specific ranges of text; as the text changes, those attributes will stick around and you can later find them using the attribute retrieval methods (I believe that's what Xcode does).

    * As an alternative, a more naïve approach could be to simply repeat the search every time you detect the text changing, but depending on the size of your text files, that could eat up a lot of CPU cycles, and is just inelegant.

    I'm sure there are other possibilities, too, but these two are the first ones that come to mind.

    HTH,

    —Mt.
  • On 4, Jul, 2012, at 03:22 PM, Marco Tabini wrote:

    >> Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.
    >
    > Couple of random suggestions:
    >
    > * If you're using NSAttributedString, you can mark your search results by assigning custom attributes to specific ranges of text; as the text changes, those attributes will stick around and you can later find them using the attribute retrieval methods (I believe that's what Xcode does).

    Ooh, I like this idea. Didn't think of that one.

    >
    > * As an alternative, a more naïve approach could be to simply repeat the search every time you detect the text changing, but depending on the size of your text files, that could eat up a lot of CPU cycles, and is just inelegant.
    >

    Yes, I thought of this too, but decided it was too inelegant to implement.

    > I'm sure there are other possibilities, too, but these two are the first ones that come to mind.

    Thanks!

    Martin

    >
    > HTH,
    >
    >
    > —Mt.
  • On 4, Jul, 2012, at 03:22 PM, Marco Tabini wrote:

    >> Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.
    >
    > Couple of random suggestions:
    >
    > * If you're using NSAttributedString, you can mark your search results by assigning custom attributes to specific ranges of text; as the text changes, those attributes will stick around and you can later find them using the attribute retrieval methods (I believe that's what Xcode does).

    Just a follow up on this. I guess I would have to clear all text attachements of a particular class from all files at the start of a search, right? Otherwise the attachments will build up over time. I don't actually save attributed text to disk (these are plain text files) but even so.  Does that make sense? I wondering how computationally expensive this will turn out to be.

    Martin

    >
    > * As an alternative, a more naïve approach could be to simply repeat the search every time you detect the text changing, but depending on the size of your text files, that could eat up a lot of CPU cycles, and is just inelegant.
    >
    > I'm sure there are other possibilities, too, but these two are the first ones that come to mind.
    >
    > HTH,
    >
    >
    > —Mt.
  • On 2012-07-04, at 1:01 PM, Martin Hewitson <martin.hewitson...> wrote:

    >
    > On 4, Jul, 2012, at 03:22 PM, Marco Tabini wrote:
    >
    >>> Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.
    >>
    >> Couple of random suggestions:
    >>
    >> * If you're using NSAttributedString, you can mark your search results by assigning custom attributes to specific ranges of text; as the text changes, those attributes will stick around and you can later find them using the attribute retrieval methods (I believe that's what Xcode does).
    >
    > Just a follow up on this. I guess I would have to clear all text attachements of a particular class from all files at the start of a search, right? Otherwise the attachments will build up over time. I don't actually save attributed text to disk (these are plain text files) but even so.  Does that make sense? I wondering how computationally expensive this will turn out to be.

    That's correct. My experience has been that NSMutableAttributedString's performance is pretty good, but YMMV depending on platform, complexity, and size of the data. In my case, I wrote a Markdown syntax highlighter that could handily manage multi-MB text files on a run-of-the-mill Macbook. I guess there's no way to tell until you try :-)

    —Mt.
  • On 4, Jul, 2012, at 07:32 PM, Marco Tabini wrote:

    >
    > On 2012-07-04, at 1:01 PM, Martin Hewitson <martin.hewitson...> wrote:
    >
    >>
    >> On 4, Jul, 2012, at 03:22 PM, Marco Tabini wrote:
    >>
    >>>> Does anyone have any good suggestions as to how to update my search results when the underlying source text changes? Do I have to listen for all changes from the underlying text objects and try to adapt, or is there a better pattern for doing this? Xcode does this nicely: no matter what changes you make in the editor, the search results seem to be updated on the fly.
    >>>
    >>> Couple of random suggestions:
    >>>
    >>> * If you're using NSAttributedString, you can mark your search results by assigning custom attributes to specific ranges of text; as the text changes, those attributes will stick around and you can later find them using the attribute retrieval methods (I believe that's what Xcode does).
    >>
    >> Just a follow up on this. I guess I would have to clear all text attachements of a particular class from all files at the start of a search, right? Otherwise the attachments will build up over time. I don't actually save attributed text to disk (these are plain text files) but even so.  Does that make sense? I wondering how computationally expensive this will turn out to be.
    >
    > That's correct. My experience has been that NSMutableAttributedString's performance is pretty good, but YMMV depending on platform, complexity, and size of the data. In my case, I wrote a Markdown syntax highlighter that could handily manage multi-MB text files on a run-of-the-mill Macbook. I guess there's no way to tell until you try :-)

    At the risk of pushing my luck too far, I would like to ask one more thing on this topic. It turns out to be pretty easy for me to add my 'match' objects to the the text storages. I just needed to make the match class (TPDocumentMatch) a subclass of NSTextAttachment. Then once I've collected all my matches for a given file, I do

      [storage beginEditing];
      for (TPDocumentMatch *match in resultDoc.matches) {
        // attach the resultDoc to the text
        [storage addAttribute:NSAttachmentAttributeName value:match range:match.range];
        NSLog(@"%@", [doc textStorage]);
      }
      [storage endEditing];
      NSLog(@"%@", [doc textStorage]);

    The problem I have is that the attachment is present according to the NSLog within the loop, but by the time we get to the NSLog outside the loop, the attachment has vanished. NSTextStorage is mutable, right? So adding my attributes like this should work, shouldn't it?

    Any further clues are very welcome and much appreciated!

    Martin

    >
    >
    > —Mt.
  • > It turns out to be pretty easy for me to add my 'match' objects to the the text storages. I just needed to make the match class (TPDocumentMatch) a subclass of NSTextAttachment. Then once I've collected all my matches for a given file, I do
    >
    > [storage beginEditing];
    > for (TPDocumentMatch *match in resultDoc.matches) {
    > // attach the resultDoc to the text
    > [storage addAttribute:NSAttachmentAttributeName value:match range:match.range];
    > NSLog(@"%@", [doc textStorage]);
    > }
    > [storage endEditing];
    > NSLog(@"%@", [doc textStorage]);
    >
    > The problem I have is that the attachment is present according to the NSLog within the loop, but by the time we get to the NSLog outside the loop, the attachment has vanished.

    Your attachments (TPDocumentMatch objects) are being removed because NSAttachmentAttributeName is only a valid attribute when applied to NSAttachmentCharacter. See:

    https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Appli
    cationKit/Classes/NSMutableAttributedString_AppKitAdditions/Reference/Refer
    ence.html#//apple_ref/occ/instm/NSMutableAttributedString/fixAttachmentAttr
    ibuteInRange
    :

    This makes sense, because your match information isn't really an attachment (which is distinct visible content the user could interact with like an image, token field cell, etc). You're just keeping track of ancillary data that happens to be applied to some text.

    Instead, you'll want to use a custom attribute/name, eg:

    NSString* TPDocumentMatchAttributeName = @"TPDocumentMatchAttribute";
    ...
    [storage addAttribute:TPDocumentMatchAttributeName value:match range:match.range];

    ~Martin
  • On Jul 4, 2012, at 5:43 PM, Martin Wierschin <martin...> wrote:

    >
    > Instead, you'll want to use a custom attribute/name, eg:
    >
    > NSString* TPDocumentMatchAttributeName = @"TPDocumentMatchAttribute";
    > ...
    > [storage addAttribute:TPDocumentMatchAttributeName value:match range:match.range];
    >

    Regarding the other solution--just do another search when the text changes--you only have to search the range that was changed, not the entire text storage, right? That doesn't seem like it would be too ungainly.

    Ross
  • Thank you all for the ideas and input. In the end the text attachment idea works perfectly. There's only one additional feature that would be nice: if a search result is deleted from the textview (which means the attachment is deleted) it would be nice to update the list of search results to remove the deleted match. I guess I could get notifications of the text storage changing, but that doesn't feel so nice. When should it stop observing the text storage? Most of the subsequent changes may have nothing to do with the search results, so checking for all text attachments each time the text storage changes feels very heavy handed. Still some more thinking to do on that part. I've noticed that Xcode doesn't update its search results when a match is deleted from the text view, so I'm not alone.

    Thanks again,

    Martin

    On 5, Jul, 2012, at 02:45 AM, Ross Carter wrote:

    > On Jul 4, 2012, at 5:43 PM, Martin Wierschin <martin...> wrote:
    >
    >>
    >> Instead, you'll want to use a custom attribute/name, eg:
    >>
    >> NSString* TPDocumentMatchAttributeName = @"TPDocumentMatchAttribute";
    >> ...
    >> [storage addAttribute:TPDocumentMatchAttributeName value:match range:match.range];
    >>
    >
    > Regarding the other solution--just do another search when the text changes--you only have to search the range that was changed, not the entire text storage, right? That doesn't seem like it would be too ungainly.
    >
    > Ross
  • > Regarding the other solution--just do another search when the text changes--you only have to search the range that was changed, not the entire text storage, right? That doesn't seem like it would be too ungainly.

    Not too ungainly, no. But then you're maintaining the matches in an external data structure, so you'd also have to adjust the ranges of all the other matches (eg: shift the starting location of all matches that come after an insertion). Not that difficult, but it could be computationally expensive for high frequency changes (eg: regular user typing) depending on how many matches you have.

    ~Martin
previous month july 2012 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today