In Xcode, if you type <# Hello, Word #>
into the text editor, it automatically gets converted to a pale-blue pill-shaped placeholder, but on disk, the text remains exactly as it was typed. Does anyone know if the same effect is achievable using NSTextView
? I've got some very ugly filepaths that must remain exactly as they are so sphinx
can put together my docs, but I want to present the user with something a little more attractive when they view the file in my custom text editor.
// This on disk (and in any other text editor)
.. image:: images/ssafs/sdfd-sdfsdg-ewfsdf.png
// This shown to the user in my custom text editor
Image of a golden eagle
As much as possible tried to write explanations as a comment in code. What I have done here.
- Found all matched links
.. image:: images/ssafs/sdfd-sdfsdg-ewfsdf1.png
with Regex and add them into an array.
- Replaced all matched links
.. image:: images/ssafs/sdfd-sdfsdg-ewfsdf1.png
with [Image] string
- Found all [Image] strings and formatted with NSMutableAttributedString as a link.
It does what you are asking and it does on the fly, your source in the database/file does not change at all.
.h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSTextViewDelegate>
@property (unsafe_unretained) IBOutlet NSTextView *aTextView;
.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//Your NSTextView
[aTextView setDelegate:(id)self];
// The Context
NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec convallis .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf1.png lacinia diam, in mattis quam egestas in. Nam gravida dolor adipiscing velit faucibus, vulputate facilisis diam facilisis. Duis id magna nibh. Proin sed turpis aliquet .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf2.png, posuere purus eget, condimentum nulla. Aenean erat odio, suscipit eu aliquet eget, porta in justo. Quisque sed sem dignissim, luctus .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf3.png libero ut, congue libero. Curabitur tristique fermentum risus in fermentum.";
//Regex to find your links .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf2.png
//You can / should improve Reges patter.
NSRegularExpression *regexPatternForFullLinks = [NSRegularExpression regularExpressionWithPattern:@"(\\.\\.\\s(.*?\\.png))"
options:NSRegularExpressionCaseInsensitive error:nil];
//Here find all image links and add them into an Array
NSArray *arrayOfAllMatches = [regexPatternForFullLinks matchesInString:string options:0 range:NSMakeRange(0, string.length)];
NSMutableArray *links = [[NSMutableArray alloc] init];
for (NSTextCheckingResult *match in arrayOfAllMatches) {
[links addObject:[[string substringWithRange:match.range] stringByReplacingOccurrencesOfString:@".. image:: " withString:@"/"]];
}
//Replacing All your links with string: [Image]
NSString *modifiedString = [regexPatternForFullLinks stringByReplacingMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
withTemplate:@"[Image]"];
NSRegularExpression *regexPatternReplaceLinksWithIMAGEStr = [NSRegularExpression regularExpressionWithPattern:@"\\[image\\]"
options:NSRegularExpressionCaseInsensitive error:nil];
NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:modifiedString];
//Here,looking for all [Image] strings and add them Link Attribute
NSArray *arrayOfAllMatchesImageText = [regexPatternReplaceLinksWithIMAGEStr matchesInString:modifiedString
options:0
range:NSMakeRange(0, modifiedString.length)];
for (int i = 0; i < arrayOfAllMatchesImageText.count; i++) {
NSTextCheckingResult *checkingResult = [arrayOfAllMatchesImageText objectAtIndex:i];
[attrString beginEditing];
[attrString addAttribute:NSLinkAttributeName value:[links objectAtIndex:i] range:checkingResult.range];
[attrString addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:checkingResult.range];
[attrString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:0] range:checkingResult.range];
[attrString endEditing];
}
//Set NSTextView Storage text...
[aTextView.textStorage setAttributedString:attrString];
}
NSTextViewDelegate: clickedOnLink to handle link clicks.
//Open Given Links with Preview App - NSTextViewDelegate
- (BOOL)textView:(NSTextView *)aTextView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex {
[[NSWorkspace sharedWorkspace] openFile:link withApplication:@"Preview"];
NSLog(@"%@", link);
return YES;
}
You can see final result on the image. If you want you can give background color to links as well.
NSTextView settings are important as well.
Update:
Just figure out this can be done more elegantly and it is efficient (faster).
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//Your NSTextView
[aTextView setDelegate:(id)self];
// The Context
NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec convallis .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf1.png lacinia diam, in mattis quam egestas in. Nam gravida dolor adipiscing velit faucibus, vulputate facilisis diam facilisis. Duis id magna nibh. Proin sed turpis aliquet .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf2.png, posuere purus eget, condimentum nulla. Aenean erat odio, suscipit eu aliquet eget, porta in justo. Quisque sed sem dignissim, luctus .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf3.png libero ut, congue libero. Curabitur tristique fermentum risus in fermentum.";
//Regex to find your links .. image:: images/ssafs/sdfd-sdfsdg-ewfsdf2.png
//You can / should improve Reges patter.
NSRegularExpression *regexPatternForFullLinks = [NSRegularExpression regularExpressionWithPattern:@"(\\.\\.\\s(.*?\\.png))"
options:NSRegularExpressionCaseInsensitive error:nil];
//Here find all image links and add them into an Array
NSArray *arrayOfAllMatches = [regexPatternForFullLinks matchesInString:string options:0 range:NSMakeRange(0, string.length)];
__block NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:string];
[arrayOfAllMatches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSTextCheckingResult *match = [arrayOfAllMatches objectAtIndex:idx];
NSString *linkValue = [[string substringWithRange:match.range] stringByReplacingOccurrencesOfString:@".. image:: " withString:@"/"];
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [NSColor greenColor],
NSUnderlineStyleAttributeName: [NSNumber numberWithInt:0],
NSLinkAttributeName:linkValue};
NSMutableAttributedString *tempAttrString = [[NSMutableAttributedString alloc] initWithString:@"[Image]" attributes:linkAttributes];
[attrString beginEditing];
[attrString replaceCharactersInRange:match.range withAttributedString:tempAttrString];
[attrString endEditing];
}];
[aTextView.textStorage setAttributedString:attrString];
}
I think you can do this sort of thing with NSTextAttachment
and a custom subclass of NSTextAttachmentCell
.
Basically create an NSAttributedString
with your and NSTextAtachment
that is using your custom NSTextAttachmentCell
Source code examples:
from the BGHUDAppKit framework
and an extension of that class for colorization from e.printstacktrace()
Edit: it looks like the NSTokenAttachmentCell
from BGHUD is actually a private apple class. so you could just use that directly if you dont need to be AppStore compliant.