From ec81778b56a48ce204033a9ca2e02a515c13a88d Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <vital.had@gmail.com>
Date: Fri, 17 Jan 2025 06:43:01 +0800
Subject: [PATCH 1/5] Revert "remove direct function calls to Obj-C methods"

This reverts commit 19d6a0cfa9cc5a4aa6539c07ade6c62a3b60dd6b.
---
 NoodleLineNumberView.h        | 10 ++++++++++
 NoodleLineNumberView.m        | 26 ++++++++++++++++++++------
 RController.h                 |  4 ++++
 RController.m                 | 31 ++++++++++++++++++++++---------
 RScriptEditorGlyphGenerator.h |  3 +++
 RScriptEditorGlyphGenerator.m | 16 ++++++++++++++++
 RScriptEditorTextStorage.h    |  7 +++++++
 RScriptEditorTextStorage.m    | 18 ++++++++++++------
 RScriptEditorTypeSetter.h     |  1 +
 RScriptEditorTypeSetter.m     |  8 ++++++--
 10 files changed, 101 insertions(+), 23 deletions(-)

diff --git NoodleLineNumberView.h NoodleLineNumberView.h
index f0f3a0f..33773a2 100644
--- NoodleLineNumberView.h
+++ NoodleLineNumberView.h
@@ -74,6 +74,16 @@ static inline id NSArrayObjectAtIndex(NSArray *self, NSUInteger i)
 	// Add support for selection by clicking/dragging
 	NSUInteger      dragSelectionStartLine;
 
+	SEL lineNumberForCharacterIndexSel;
+	IMP lineNumberForCharacterIndexIMP;
+	SEL lineRangeForRangeSel;
+	SEL numberWithUnsignedIntegerSel;
+	IMP numberWithUnsignedIntegerIMP;
+	SEL addObjectSel;
+	IMP addObjectIMP;
+	SEL rangeOfLineSel;
+	Class numberClass;
+
 	NSLayoutManager  *layoutManager;
 	NSTextContainer  *container;
 	NSTextView       *clientView;
diff --git NoodleLineNumberView.m NoodleLineNumberView.m
index 308bed5..afe71a0 100644
--- NoodleLineNumberView.m
+++ NoodleLineNumberView.m
@@ -97,8 +97,18 @@
 		[self updateGutterThicknessConstants];
 		currentRuleThickness = 0.0f;
 
+		// Cache loop methods for speed
+		lineNumberForCharacterIndexSel = @selector(lineNumberForCharacterIndex:);
+		lineNumberForCharacterIndexIMP = [self methodForSelector:lineNumberForCharacterIndexSel];
+		lineRangeForRangeSel = @selector(lineRangeForRange:);
+		addObjectSel = @selector(addObject:);
+		numberWithUnsignedIntegerSel = @selector(numberWithUnsignedInteger:);
+		numberWithUnsignedIntegerIMP = [NSNumber methodForSelector:numberWithUnsignedIntegerSel];
+		rangeOfLineSel = @selector(getLineStart:end:contentsEnd:forRange:);
+
 		currentNumberOfLines = 1;
 		lineWrapping = NO;
+		numberClass = [NSNumber class];
 		
 		normalBackgroundColor = [[NSColor colorWithCalibratedWhite: 0.95 alpha: 1.0] retain];
 		foldedBackgroundColor = [[NSColor colorWithCalibratedWhite: 0.85 alpha: 1.0] retain];
@@ -282,7 +292,7 @@
 	// It doesn't show up in the glyphs so would not be accounted for.
 	range.length++;
 
-	for (line = [self lineNumberForCharacterIndex: range.location]; line < count; line++)
+	for (line = (NSUInteger)(*lineNumberForCharacterIndexIMP)(self, lineNumberForCharacterIndexSel, range.location); line < count; line++)
 	{
 
 		rects = [layoutManager rectArrayForCharacterRange:NSMakeRange([NSArrayObjectAtIndex(lines, line) unsignedIntegerValue], 0)
@@ -386,7 +396,7 @@
 	BOOL flipped = [self isFlipped];
 
 
-	for (line = [self lineNumberForCharacterIndex: range.location]; line < count; line++)
+	for (line = (NSUInteger)(*lineNumberForCharacterIndexIMP)(self, lineNumberForCharacterIndexSel, range.location); line < count; line++)
 	{
 		index = [NSArrayObjectAtIndex(lines, line) unsignedIntegerValue];
 
@@ -777,17 +787,21 @@
 
 	index = 0;
 
+	// Cache loop methods for speed
+	IMP rangeOfLineIMP = [textString methodForSelector:rangeOfLineSel];
+	addObjectIMP = [lineIndices methodForSelector:addObjectSel];
+
 	do
 	{
-        [lineIndices addObject: [NSNumber numberWithUnsignedInteger: index]];
-        [textString getLineStart:NULL end:&index contentsEnd:NULL forRange:NSMakeRange(index, 0)];
+		(void)(*addObjectIMP)(lineIndices, addObjectSel, (*numberWithUnsignedIntegerIMP)(numberClass, numberWithUnsignedIntegerSel, index));
+		(*rangeOfLineIMP)(textString, rangeOfLineSel, NULL, &index, NULL, NSMakeRange(index, 0));
 	}
 	while (index < stringLength);
 
 	// Check if text ends with a new line.
-    [textString getLineStart:NULL end:&lineEnd contentsEnd:&contentEnd forRange:NSMakeRange([[lineIndices lastObject] intValue], 0)];
+	(*rangeOfLineIMP)(textString, rangeOfLineSel, NULL, &lineEnd, &contentEnd, NSMakeRange([[lineIndices lastObject] intValue], 0));
 	if (contentEnd < lineEnd)
-        [lineIndices addObject: [NSNumber numberWithUnsignedInteger: index]];
+		(void)(*addObjectIMP)(lineIndices, addObjectSel, (*numberWithUnsignedIntegerIMP)(numberClass, numberWithUnsignedIntegerSel, index));
 
 	NSUInteger lineCount = [lineIndices count];
 	if(lineCount < 100)
diff --git RController.h RController.h
index 0fba2c8..1648ead 100644
--- RController.h
+++ RController.h
@@ -92,6 +92,10 @@
 	WebView *currentWebViewForFindAction;
 	id searchInWebViewWindow;
 
+	IMP _nextEventImp;
+	IMP _sendEventImp;
+	IMP _doProcessImp;
+
 	NSTimer *timer;
 	NSTimer *RLtimer;
 	NSTimer *Flushtimer;
diff --git RController.m RController.m
index 12df46b..0c19f7e 100644
--- RController.m
+++ RController.m
@@ -115,6 +115,10 @@ int R_SetOptionWidth(int);
 
 static RController* sharedRController;
 
+static SEL _nextEventSel;
+static SEL _sendEventSel;
+static SEL _doProcessSel;
+
 static inline const char* NSStringUTF8String(NSString* self) 
 {
 	typedef const char* (*SPUTF8StringMethodPtr)(NSString*, SEL);
@@ -212,6 +216,14 @@ static inline const char* NSStringUTF8String(NSString* self)
 
 	filteredHistory = nil;
 	
+	_nextEventSel = @selector(nextEventMatchingMask:untilDate:inMode:dequeue:);
+	_sendEventSel = @selector(sendEvent:);
+	_doProcessSel = @selector(doProcessEvents:);
+
+	_nextEventImp = [NSApp methodForSelector:_nextEventSel];
+	_sendEventImp = [NSApp methodForSelector:_sendEventSel];
+	_doProcessImp = [self methodForSelector:_doProcessSel];
+
 	specialCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"\r\b\a"] retain];
 
 	[[NSNotificationCenter defaultCenter] addObserver:self 
@@ -1402,7 +1414,7 @@ extern BOOL isTimeToFinish;
 	
 	while ([consoleInputQueue count]==0) {
 		processingEvents = NO; // we should be at the top level, so for sanity reasons make sure we always process events
-		[self doProcessEvents: YES];
+		(_doProcessImp)(self, _doProcessSel, YES);
 	}
 	
 	currentConsoleInput = [consoleInputQueue objectAtIndex:0];
@@ -1703,8 +1715,9 @@ extern BOOL isTimeToFinish;
 			// to be processed in all cases, even if system was called from within
 			// the event handler and as such can run recursively
 			NSEvent *event;
-			if((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.05] inMode:NSDefaultRunLoopMode dequeue:YES]))
-				[NSApp sendEvent: event];
+			if((event = (_nextEventImp)(NSApp, _nextEventSel, NSAnyEventMask, [NSDate dateWithTimeIntervalSinceNow:0.05], NSDefaultRunLoopMode, YES)))
+				(_sendEventImp)(NSApp, _sendEventSel, event);
+
 		}
 		if(breakPending) {
 			kill(pid, SIGINT);
@@ -3100,11 +3113,11 @@ outputType: 0 = stdout, 1 = stderr, 2 = stdout/err as root
 #ifdef USE_POOLS
 		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 #endif
-		if (blocking)
-            [NSApp sendEvent: [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]];
-		else {
-            while((event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001] inMode:NSDefaultRunLoopMode dequeue:YES]))
-				[NSApp sendEvent: event];
+		if (blocking){
+			(_sendEventImp)(NSApp, _sendEventSel, (_nextEventImp)(NSApp, _nextEventSel, NSAnyEventMask, [NSDate distantFuture], NSDefaultRunLoopMode, YES));
+		} else {
+			while((event = (_nextEventImp)(NSApp, _nextEventSel, NSAnyEventMask, [NSDate dateWithTimeIntervalSinceNow:0.0001], NSDefaultRunLoopMode, YES)))
+				(_sendEventImp)(NSApp, _sendEventSel, event);
 		}
 #ifdef USE_POOLS
 		[pool release];
@@ -3132,7 +3145,7 @@ outputType: 0 = stdout, 1 = stderr, 2 = stdout/err as root
 
 - (void) handleProcessEvents
 {
-	[self doProcessEvents: NO];
+	(_doProcessImp)(self, _doProcessSel, NO);
 }
 
 
diff --git RScriptEditorGlyphGenerator.h RScriptEditorGlyphGenerator.h
index c9cab1e..80251cc 100644
--- RScriptEditorGlyphGenerator.h
+++ RScriptEditorGlyphGenerator.h
@@ -42,6 +42,9 @@
 	id <NSGlyphStorage> _destination;
 	RScriptEditorTextStorage *theTextStorage;
 	NSGlyph nullGlyph;
+	IMP _attrStrImp;
+	IMP _foldImp;
+	IMP _foldindImp;
 	NSInteger sizeOfNSGlyph;
 
 }
diff --git RScriptEditorGlyphGenerator.m RScriptEditorGlyphGenerator.m
index 464769c..82d0ab6 100644
--- RScriptEditorGlyphGenerator.m
+++ RScriptEditorGlyphGenerator.m
@@ -38,13 +38,27 @@
 #import "RScriptEditorLayoutManager.h"
 #import "PreferenceKeys.h"
 
+static SEL _attrStrSel;
+static SEL _foldSel;
+static SEL _foldindSel;
+
 @implementation RScriptEditorGlyphGenerator
 
++ (void)initialize
+{
+	if ([self class] == [RScriptEditorGlyphGenerator class]) {
+		_attrStrSel = @selector(attributedString);
+		_foldSel    = @selector(foldedRangeAtIndex:);
+		_foldindSel = @selector(foldedForIndicatorAtIndex:);
+	}
+}
+
 - (id)init
 {
 	self = [super init];
 
 	if (self != nil) {
+		_attrStrImp = [self methodForSelector:_attrStrSel];
 		nullGlyph = NSNullGlyph;
 		sizeOfNSGlyph = sizeof(NSGlyph);
 	}
@@ -61,6 +75,8 @@
 {
 	if(theTextStorage) [theTextStorage release];
 	theTextStorage = [textStorage retain];
+	_foldImp = [theTextStorage methodForSelector:_foldSel];
+	_foldindImp = [theTextStorage methodForSelector:_foldindSel];
 }
 
 - (void)generateGlyphsForGlyphStorage:(id <NSGlyphStorage>)glyphStorage desiredNumberOfCharacters:(NSUInteger)nChars glyphIndex:(NSUInteger *)glyphIndex characterIndex:(NSUInteger *)charIndex
diff --git RScriptEditorTextStorage.h RScriptEditorTextStorage.h
index f04f993..2879ca9 100644
--- RScriptEditorTextStorage.h
+++ RScriptEditorTextStorage.h
@@ -46,6 +46,13 @@
 	
 	id selfDelegate;
 
+	IMP _getImp;
+	IMP _setImp;
+	IMP _strImp;
+	IMP _replImp;
+	IMP _editImp;
+	IMP _getlImp;
+
 }
 
 - (id)initWithDelegate:(id)theDelegate;
diff --git RScriptEditorTextStorage.m RScriptEditorTextStorage.m
index e52d397..65d9844 100644
--- RScriptEditorTextStorage.m
+++ RScriptEditorTextStorage.m
@@ -77,6 +77,12 @@ static SEL _getlSel;
 
 	if (self != nil) {
 		_attributedString = [[NSTextStorage alloc] init];
+		_getImp  = [_attributedString methodForSelector:_getSel];
+		_setImp  = [_attributedString methodForSelector:_setSel];
+		_strImp  = [_attributedString methodForSelector:_strSel];
+		_replImp = [_attributedString methodForSelector:_replSel];
+		_editImp = [self methodForSelector:_editSel];
+		_getlImp = [_attributedString methodForSelector:_getlSel];
 
 		selfDelegate = (RScriptEditorTextView*)theDelegate;
 		[self setDelegate:theDelegate];
@@ -298,13 +304,13 @@ static SEL _getlSel;
 
 - (NSString *)string
 { 
-    return _attributedString ? [_attributedString string] : nil;
+	return (*_strImp)(_attributedString, _strSel);
 }
 
 - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
 {
 
-    NSDictionary *attributes = [_attributedString attributesAtIndex:location effectiveRange:range];
+	NSDictionary *attributes = (*_getImp)(_attributedString, _getSel, location, range);
 
 	if(!foldedCounter || location > [_attributedString length]) return attributes;
 
@@ -375,14 +381,14 @@ static SEL _getlSel;
 // NSMutableAttributedString primitives
 - (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
 {
-    [_attributedString replaceCharactersInRange:range withString:str];
-    [self edited:NSTextStorageEditedCharacters range:range changeInLength: [str length] - range.length];
+	(*_replImp)(_attributedString, _replSel, range, str);
+	(*_editImp)(self, _editSel, NSTextStorageEditedCharacters, range, [str length] - range.length);
 }
 
 - (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
 {
-    [_attributedString setAttributes:attrs range:range];
-    [self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
+	(*_setImp)(_attributedString, _setSel, attrs, range);
+	(*_editImp)(self, _editSel, NSTextStorageEditedAttributes, range, 0);
 }
 
 // Attribute Fixing Overrides
diff --git RScriptEditorTypeSetter.h RScriptEditorTypeSetter.h
index a53688f..ded33e9 100644
--- RScriptEditorTypeSetter.h
+++ RScriptEditorTypeSetter.h
@@ -42,6 +42,7 @@
 {
 
 	RScriptEditorTextStorage *_attributedString;
+	IMP _foldImp;
 
 }
 
diff --git RScriptEditorTypeSetter.m RScriptEditorTypeSetter.m
index e9a20c7..b49771c 100644
--- RScriptEditorTypeSetter.m
+++ RScriptEditorTypeSetter.m
@@ -38,11 +38,14 @@
 
 @implementation RScriptEditorTypeSetter
 
+static SEL _foldSel;
+
 - (id)init
 {
 	self = [super init];
 	if (nil == self) return nil;
 	_attributedString = nil;
+	_foldSel = @selector(foldedForIndicatorAtIndex:);
 	return self;
 }
 
@@ -56,12 +59,13 @@
 {
 	if(_attributedString) [_attributedString release];
 	_attributedString = [textStorage retain];
+	_foldImp  = [_attributedString methodForSelector:_foldSel];
+
 }
 
 - (NSTypesetterControlCharacterAction)actionForControlCharacterAtIndex:(NSUInteger)charIndex
 {
-    if (_attributedString && [_attributedString foldedForIndicatorAtIndex:charIndex] > -1)
-        return NSTypesetterZeroAdvancementAction;
+	if (_foldImp && (NSInteger)(*_foldImp)(_attributedString, _foldSel, charIndex) > -1) return NSTypesetterZeroAdvancementAction;
 	return [super actionForControlCharacterAtIndex:charIndex];
 }
 
-- 
2.48.0

