mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
Rework symbol chooser code
The symbol chooser is now owned by DocumentView instead of DocumentController. The symbol chooser does not know about its owner and is not an observer of any notifications. Instead it has a property for “document” and one for “selectionString”. It is the owners job to update these properties (i.e. when switching document or changing the selection).
This commit is contained in:
@@ -58,7 +58,6 @@ PUBLIC @interface DocumentController : NSObject
|
||||
// =============================
|
||||
|
||||
- (IBAction)orderFrontFindPanel:(id)sender;
|
||||
- (IBAction)showSymbolChooser:(id)sender;
|
||||
- (IBAction)goToFile:(id)sender;
|
||||
|
||||
// ==================
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#import <OakFileBrowser/OakFileBrowser.h>
|
||||
#import <HTMLOutputWindow/HTMLOutputWindow.h>
|
||||
#import <OakFilterList/FileChooser.h>
|
||||
#import <OakFilterList/SymbolChooser.h>
|
||||
#import <OakSystem/application.h>
|
||||
#import <Find/Find.h>
|
||||
#import <file/path_info.h>
|
||||
@@ -68,8 +67,6 @@ static BOOL IsInShouldTerminateEventLoop = NO;
|
||||
@property (nonatomic) NSString* pathAttributes;
|
||||
@property (nonatomic) NSString* projectPath;
|
||||
|
||||
@property (nonatomic) OakFilterWindowController* filterWindowController;
|
||||
|
||||
@property (nonatomic) NSArray* urlArrayForQuickLook;
|
||||
|
||||
+ (void)scheduleSessionBackup:(id)sender;
|
||||
@@ -267,7 +264,6 @@ namespace
|
||||
self.tabBarView.dataSource = nil;
|
||||
self.tabBarView.delegate = nil;
|
||||
self.textView.delegate = nil;
|
||||
self.filterWindowController = nil; // ensures we removeObserver: and set target to nil
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)aNotification
|
||||
@@ -1384,27 +1380,6 @@ namespace
|
||||
// = Opening Auxiliary Windows =
|
||||
// =============================
|
||||
|
||||
- (void)setFilterWindowController:(OakFilterWindowController*)controller
|
||||
{
|
||||
if(_filterWindowController != controller)
|
||||
{
|
||||
if(_filterWindowController)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_filterWindowController.window];
|
||||
_filterWindowController.target = nil;
|
||||
[_filterWindowController close];
|
||||
}
|
||||
|
||||
if(_filterWindowController = controller)
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterWindowWillClose:) name:NSWindowWillCloseNotification object:_filterWindowController.window];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)filterWindowWillClose:(NSNotification*)notification
|
||||
{
|
||||
self.filterWindowController = nil;
|
||||
}
|
||||
|
||||
- (IBAction)orderFrontFindPanel:(id)sender
|
||||
{
|
||||
Find* find = [Find sharedInstance];
|
||||
@@ -1454,20 +1429,6 @@ namespace
|
||||
[find showFindPanel:self];
|
||||
}
|
||||
|
||||
- (IBAction)showSymbolChooser:(id)sender
|
||||
{
|
||||
self.filterWindowController = [OakFilterWindowController new];
|
||||
self.filterWindowController.dataSource = [SymbolChooser symbolChooserForDocumentView:[self documentView]];
|
||||
self.filterWindowController.action = @selector(symbolChooserDidSelectItems:);
|
||||
self.filterWindowController.sendActionOnSingleClick = YES;
|
||||
[self.filterWindowController showWindowRelativeToWindow:self.window];
|
||||
}
|
||||
|
||||
- (void)symbolChooserDidSelectItems:(id)sender
|
||||
{
|
||||
[self openItems:[sender selectedItems] closingOtherTabs:NO];
|
||||
}
|
||||
|
||||
// ==================
|
||||
// = OakFileChooser =
|
||||
// ==================
|
||||
|
||||
@@ -3,21 +3,11 @@
|
||||
#else
|
||||
#import "../OakFilterList.h"
|
||||
#endif
|
||||
#import <OakTextView/OakDocumentView.h>
|
||||
#import <document/document.h>
|
||||
#import <oak/misc.h>
|
||||
|
||||
@class SymbolChooserViewController;
|
||||
|
||||
PUBLIC @interface SymbolChooser : NSObject <FilterListDataSource>
|
||||
{
|
||||
OBJC_WATCH_LEAKS(SymbolChooser);
|
||||
document::document_ptr document;
|
||||
OakDocumentView* documentView;
|
||||
std::string filterString;
|
||||
SymbolChooserViewController* viewController;
|
||||
NSArray* _items;
|
||||
}
|
||||
+ (id)symbolChooserForDocumentView:(OakDocumentView *)aDocumentView;
|
||||
@property (nonatomic, readonly) NSString* filterString;
|
||||
@property (nonatomic) document::document_ptr const& document;
|
||||
@property (nonatomic, retain) NSString* selectionString;
|
||||
+ (id)symbolChooserForDocument:(document::document_ptr)aDocument;
|
||||
@end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#import "SymbolChooser.h"
|
||||
#import "SymbolList.h"
|
||||
#import <OakFoundation/NSString Additions.h>
|
||||
#import <OakTextView/OakDocumentView.h>
|
||||
#import <text/ranker.h>
|
||||
#import <text/case.h>
|
||||
#import "../highlight_ranges.h"
|
||||
#import <ns/ns.h>
|
||||
|
||||
OAK_DEBUG_VAR(FilterList_SymbolChooser);
|
||||
|
||||
@@ -48,83 +48,115 @@ OAK_DEBUG_VAR(FilterList_SymbolChooser);
|
||||
@end
|
||||
|
||||
@interface SymbolChooser ()
|
||||
- (void)updateSymbols;
|
||||
{
|
||||
OBJC_WATCH_LEAKS(SymbolChooser);
|
||||
document::document_ptr _document;
|
||||
SymbolChooserViewController* _viewController;
|
||||
}
|
||||
@property (nonatomic) NSArray* items;
|
||||
@property (nonatomic) NSArray* selectedItems;
|
||||
@property (nonatomic) NSString* filterString;
|
||||
@property (nonatomic) BOOL needsReload;
|
||||
@end
|
||||
|
||||
@implementation SymbolChooser
|
||||
- (NSViewController*)viewController
|
||||
+ (id)symbolChooserForDocument:(document::document_ptr)aDocument
|
||||
{
|
||||
if(!viewController)
|
||||
viewController = [[SymbolChooserViewController alloc] initWithSymbolChooser:self];
|
||||
return viewController;
|
||||
SymbolChooser* res = [SymbolChooser new];
|
||||
res.document = aDocument;
|
||||
return res;
|
||||
}
|
||||
|
||||
- (id)initWithDocumentView:(OakDocumentView *)aDocumentView
|
||||
- (void)dealloc
|
||||
{
|
||||
if(self = [super init])
|
||||
self.document = document::document_ptr();
|
||||
}
|
||||
|
||||
- (void)updateItemsArray
|
||||
{
|
||||
self.selectedItems = nil;
|
||||
self.items = SymbolListForDocument(_document, to_s(_filterString));
|
||||
[self updateSelectedItemsArray];
|
||||
}
|
||||
|
||||
- (void)updateSelectedItemsArray
|
||||
{
|
||||
if(!_selectionString || [_selectionString isEqualToString:@""])
|
||||
{
|
||||
struct callback_t : document::open_callback_t
|
||||
{
|
||||
callback_t (SymbolChooser* self) : _self(self) { }
|
||||
|
||||
void show_error (std::string const& path, document::document_ptr document, std::string const& message, oak::uuid_t const& filter)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", path.c_str(), message.c_str());
|
||||
}
|
||||
|
||||
void show_document (std::string const& path, document::document_ptr document)
|
||||
{
|
||||
[_self updateSymbols];
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolChooser* _self;
|
||||
};
|
||||
|
||||
document = [aDocumentView document];
|
||||
documentView = aDocumentView;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSymbols) name:FLDataSourceItemsDidChangeNotification object:documentView];
|
||||
if(document->try_open(document::open_callback_ptr((document::open_callback_t*)new callback_t(self))))
|
||||
[self updateSymbols];
|
||||
self.selectedItems = nil;
|
||||
return;
|
||||
}
|
||||
return self;
|
||||
|
||||
NSMutableArray* res = [NSMutableArray array];
|
||||
|
||||
std::map<text::pos_t, FileChooserSymbolItem*> symbolItems;
|
||||
for(FileChooserSymbolItem* item in _items)
|
||||
{
|
||||
text::selection_t sel(to_s(item.selectionString));
|
||||
text::pos_t pos = sel.last().min();
|
||||
symbolItems.insert(std::make_pair(pos, item));
|
||||
}
|
||||
|
||||
for(text::range_t const& range : text::selection_t(to_s(_selectionString)))
|
||||
{
|
||||
auto it = symbolItems.upper_bound(range.min());
|
||||
if(it != symbolItems.begin())
|
||||
[res addObject:(--it)->second];
|
||||
}
|
||||
|
||||
self.selectedItems = res;
|
||||
}
|
||||
|
||||
+ (id)symbolChooserForDocumentView:(OakDocumentView *)aDocumentView
|
||||
- (void)delayedReload:(id)sender
|
||||
{
|
||||
return [[SymbolChooser alloc] initWithDocumentView:aDocumentView];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
|
||||
self.needsReload = NO;
|
||||
}
|
||||
|
||||
- (void)setNeedsReload:(BOOL)flag
|
||||
{
|
||||
if(_needsReload == flag)
|
||||
return;
|
||||
if(_needsReload = flag)
|
||||
[self performSelector:@selector(delayedReload:) withObject:self afterDelay:0];
|
||||
}
|
||||
|
||||
- (void)setItems:(NSArray*)anArray
|
||||
{
|
||||
_items = anArray;
|
||||
self.needsReload = YES;
|
||||
}
|
||||
|
||||
- (void)setSelectedItems:(NSArray*)anArray
|
||||
{
|
||||
_selectedItems = anArray;
|
||||
self.needsReload = YES;
|
||||
}
|
||||
|
||||
// ==================
|
||||
// = Action methods =
|
||||
// ==================
|
||||
|
||||
- (IBAction)search:(id)sender
|
||||
{
|
||||
ASSERT([sender respondsToSelector:@selector(stringValue)]);
|
||||
self.filterString = [[sender stringValue] lowercaseString];
|
||||
}
|
||||
|
||||
// ========================
|
||||
// = FilterListDataSource =
|
||||
// ========================
|
||||
|
||||
- (NSString*)title
|
||||
{
|
||||
return @"Go to Symbol";
|
||||
}
|
||||
|
||||
- (NSString*)filterString
|
||||
- (NSViewController*)viewController
|
||||
{
|
||||
return [NSString stringWithCxxString:filterString];
|
||||
}
|
||||
|
||||
- (void)updateSymbols
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
|
||||
}
|
||||
|
||||
- (IBAction)search:(id)sender
|
||||
{
|
||||
ASSERT([sender respondsToSelector:@selector(stringValue)]);
|
||||
std::string const& newFilterString = [[[sender stringValue] lowercaseString] UTF8String];
|
||||
if(newFilterString != filterString)
|
||||
{
|
||||
filterString = newFilterString;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:FLDataSourceItemsDidChangeNotification object:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*)items
|
||||
{
|
||||
_items = SymbolListForDocument(document, filterString);
|
||||
return _items;
|
||||
if(!_viewController)
|
||||
_viewController = [[SymbolChooserViewController alloc] initWithSymbolChooser:self];
|
||||
return _viewController;
|
||||
}
|
||||
|
||||
- (NSAttributedString*)displayStringForItem:(id)item
|
||||
@@ -137,43 +169,55 @@ OAK_DEBUG_VAR(FilterList_SymbolChooser);
|
||||
return [item infoString];
|
||||
}
|
||||
|
||||
- (NSArray*)selectedItems
|
||||
// =======
|
||||
// = API =
|
||||
// =======
|
||||
|
||||
- (void)setDocument:(document::document_ptr const&)aDocument
|
||||
{
|
||||
text::selection_t sel([[documentView textView].selectionString UTF8String]);
|
||||
ng::buffer_t const& buf = document->buffer();
|
||||
size_t min = buf.convert(sel.last().min());
|
||||
size_t max = buf.convert(sel.last().max());
|
||||
BOOL past_selection = NO;
|
||||
NSUInteger last_index;
|
||||
NSUInteger index = 0;
|
||||
NSMutableIndexSet* indexesToSelect = [NSMutableIndexSet indexSet];
|
||||
for (id item in _items)
|
||||
struct callback_t : document::open_callback_t
|
||||
{
|
||||
text::selection_t pos([[item selectionString] UTF8String]);
|
||||
size_t start = buf.convert(pos.last().min());
|
||||
size_t end = buf.convert(pos.last().max());
|
||||
last_index = index;
|
||||
index = [_items indexOfObject:item];
|
||||
if (start > min)
|
||||
callback_t (SymbolChooser* self) : _self(self) { }
|
||||
|
||||
void show_error (std::string const& path, document::document_ptr document, std::string const& message, oak::uuid_t const& filter)
|
||||
{
|
||||
if (!past_selection && last_index != NSNotFound)
|
||||
[indexesToSelect addIndex:last_index];
|
||||
past_selection = YES;
|
||||
if (end <= max)
|
||||
{
|
||||
if(index != NSNotFound)
|
||||
[indexesToSelect addIndex:index];
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", path.c_str(), message.c_str());
|
||||
}
|
||||
|
||||
void show_document (std::string const& path, document::document_ptr document)
|
||||
{
|
||||
[_self updateItemsArray];
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolChooser* _self;
|
||||
};
|
||||
|
||||
if(_document)
|
||||
_document->close();
|
||||
|
||||
if(_document = aDocument)
|
||||
{
|
||||
if(_document->try_open(document::open_callback_ptr((document::open_callback_t*)new callback_t(self))))
|
||||
[self updateItemsArray];
|
||||
}
|
||||
if (!past_selection)
|
||||
[indexesToSelect addIndex:index];
|
||||
return [_items objectsAtIndexes:indexesToSelect];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
- (void)setSelectionString:(NSString*)aString
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:FLDataSourceItemsDidChangeNotification object:documentView];
|
||||
document->close();
|
||||
if(_selectionString != aString && ![_selectionString isEqualToString:aString])
|
||||
{
|
||||
_selectionString = aString;
|
||||
[self updateSelectedItemsArray];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFilterString:(NSString*)aString
|
||||
{
|
||||
if(_filterString != aString && ![_filterString isEqualToString:aString])
|
||||
{
|
||||
_filterString = aString;
|
||||
[self updateItemsArray];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SOURCES = src/*.mm src/datasources/*.{cc,mm}
|
||||
EXPORT = src/OakFilterList.h src/FileChooser.h src/datasources/*.h
|
||||
CP_Resources = resources/*
|
||||
LINK += text bundles document io plist MGScopeBar ns OakAppKit OakFoundation OakSystem OakTextView regexp scope OakFileBrowser
|
||||
LINK += text bundles document io plist MGScopeBar ns OakAppKit OakFoundation OakSystem regexp scope OakFileBrowser
|
||||
FRAMEWORKS = Cocoa Carbon
|
||||
|
||||
@@ -38,4 +38,6 @@ PUBLIC @interface OakDocumentView : NSView <GutterViewDelegate, GutterViewColumn
|
||||
|
||||
- (void)addAuxiliaryView:(NSView*)aView atEdge:(NSRectEdge)anEdge;
|
||||
- (void)removeAuxiliaryView:(NSView*)aView;
|
||||
|
||||
- (IBAction)showSymbolChooser:(id)sender;
|
||||
@end
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#import <ns/ns.h>
|
||||
#import <oak/debug.h>
|
||||
#import <bundles/bundles.h>
|
||||
#import <OakFilterList/SymbolChooser.h>
|
||||
#import <OakFoundation/NSString Additions.h>
|
||||
#import <OakAppKit/OakAppKit.h>
|
||||
#import <OakAppKit/NSColor Additions.h>
|
||||
@@ -36,6 +37,8 @@ static NSString* const kFoldingsColumnIdentifier = @"foldings";
|
||||
@property (nonatomic, retain) NSDictionary* gutterImages;
|
||||
@property (nonatomic, retain) NSDictionary* gutterHoverImages;
|
||||
@property (nonatomic, retain) NSDictionary* gutterPressedImages;
|
||||
@property (nonatomic) OakFilterWindowController* filterWindowController;
|
||||
@property (nonatomic) SymbolChooser* symbolChooser;
|
||||
- (void)updateStyle;
|
||||
@end
|
||||
|
||||
@@ -231,12 +234,12 @@ private:
|
||||
char const* str = [[textView valueForKey:@"selectionString"] UTF8String] ?: "1";
|
||||
[gutterView setHighlightedRange:str];
|
||||
[statusBar setCaretPosition:str];
|
||||
_symbolChooser.selectionString = textView.selectionString;
|
||||
|
||||
ng::buffer_t const& buf = document->buffer();
|
||||
text::selection_t sel([textView.selectionString UTF8String]);
|
||||
size_t i = buf.convert(sel.last().max());
|
||||
statusBar.symbolName = [NSString stringWithCxxString:buf.symbol_at(i)];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"FLDataSourceItemsDidChangeNotification" object:self];
|
||||
}
|
||||
else if([aKeyPath isEqualToString:@"tabSize"])
|
||||
{
|
||||
@@ -265,6 +268,9 @@ private:
|
||||
[self setDocument:document::document_ptr()];
|
||||
delete callback;
|
||||
|
||||
self.filterWindowController = nil;
|
||||
self.symbolChooser = nil;
|
||||
|
||||
[gutterScrollView release];
|
||||
[gutterView release];
|
||||
[gutterDividerView release];
|
||||
@@ -311,6 +317,9 @@ private:
|
||||
[gutterView reloadData:self];
|
||||
[self updateStyle];
|
||||
|
||||
if(_symbolChooser)
|
||||
_symbolChooser.document = document;
|
||||
|
||||
if(oldDocument)
|
||||
{
|
||||
oldDocument->hide();
|
||||
@@ -457,6 +466,56 @@ private:
|
||||
[[OakPasteboard pasteboardWithName:NSFindPboard] selectItemAtPosition:[textView positionForWindowUnderCaret] andCall:@selector(findNext:)];
|
||||
}
|
||||
|
||||
// ==================
|
||||
// = Symbol Chooser =
|
||||
// ==================
|
||||
|
||||
- (void)setFilterWindowController:(OakFilterWindowController*)controller
|
||||
{
|
||||
if(_filterWindowController == controller)
|
||||
return;
|
||||
|
||||
if(_filterWindowController)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_filterWindowController.window];
|
||||
_filterWindowController.target = nil;
|
||||
[_filterWindowController close];
|
||||
}
|
||||
|
||||
if(_filterWindowController = controller)
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(filterWindowWillClose:) name:NSWindowWillCloseNotification object:_filterWindowController.window];
|
||||
}
|
||||
|
||||
- (void)filterWindowWillClose:(NSNotification*)notification
|
||||
{
|
||||
self.filterWindowController = nil;
|
||||
self.symbolChooser = nil;
|
||||
}
|
||||
|
||||
- (IBAction)showSymbolChooser:(id)sender
|
||||
{
|
||||
if(self.filterWindowController)
|
||||
{
|
||||
[self.filterWindowController.window makeKeyAndOrderFront:self];
|
||||
return;
|
||||
}
|
||||
|
||||
self.symbolChooser = [SymbolChooser symbolChooserForDocument:document];
|
||||
self.symbolChooser.selectionString = textView.selectionString;
|
||||
|
||||
self.filterWindowController = [OakFilterWindowController new];
|
||||
self.filterWindowController.dataSource = self.symbolChooser;
|
||||
self.filterWindowController.action = @selector(symbolChooserDidSelectItems:);
|
||||
self.filterWindowController.sendActionOnSingleClick = YES;
|
||||
[self.filterWindowController showWindowRelativeToWindow:self.window];
|
||||
}
|
||||
|
||||
- (void)symbolChooserDidSelectItems:(id)sender
|
||||
{
|
||||
for(id item in [sender selectedItems])
|
||||
[textView setSelectionString:[item selectionString]];
|
||||
}
|
||||
|
||||
// =======================
|
||||
// = Status bar delegate =
|
||||
// =======================
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SOURCES = src/*.{cc,mm}
|
||||
EXPORT = src/OakDocumentView.h src/GutterView.h src/OakTextView.h
|
||||
CP_Resources = resources/*
|
||||
LINK += layout text bundles editor document theme ns plist OakAppKit OakFoundation OakSystem regexp scope settings BundleMenu
|
||||
LINK += layout text bundles editor document theme ns plist OakAppKit OakFoundation OakSystem regexp scope settings BundleMenu OakFilterList
|
||||
FRAMEWORKS = Cocoa
|
||||
|
||||
OBJCXX_FLAGS += -fno-objc-arc
|
||||
|
||||
Reference in New Issue
Block a user