Revision: 14175
          http://sourceforge.net/p/skim-app/code/14175
Author:   hofman
Date:     2024-04-04 16:12:03 +0000 (Thu, 04 Apr 2024)
Log Message:
-----------
Let sync parser classes load the file in all cases. Conform classes to common 
protocol, and loead them in unified mwethod. Let synchronizer keep track of 
resolver source file names fo all parsers. Move concrete parsers to separate 
files.

Modified Paths:
--------------
    trunk/SKPDFSynchronizer.h
    trunk/SKPDFSynchronizer.m
    trunk/Skim.xcodeproj/project.pbxproj

Added Paths:
-----------
    trunk/SKPDFSyncParser.h
    trunk/SKPDFSyncParser.m
    trunk/SKSyncTeXParser.h
    trunk/SKSyncTeXParser.m

Added: trunk/SKPDFSyncParser.h
===================================================================
--- trunk/SKPDFSyncParser.h                             (rev 0)
+++ trunk/SKPDFSyncParser.h     2024-04-04 16:12:03 UTC (rev 14175)
@@ -0,0 +1,54 @@
+//
+//  SKPDFSyncParser.h
+//  Skim
+//
+//  Created by Christiaan Hofman on 04/04/2024.
+/*
+ This software is Copyright (c) 2024
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "SKPDFSynchronizer.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class SKPDFSyncRecord;
+
+@interface SKPDFSyncParser : NSObject <SKSyncParser> {
+    NSMutableArray<NSMutableArray<SKPDFSyncRecord *> *> *pages;
+    NSMapTable *lines;
+    NSString *syncFileName;
+}
+- (void)loadPdfsyncFile;
+@end
+
+NS_ASSUME_NONNULL_END

Added: trunk/SKPDFSyncParser.m
===================================================================
--- trunk/SKPDFSyncParser.m                             (rev 0)
+++ trunk/SKPDFSyncParser.m     2024-04-04 16:12:03 UTC (rev 14175)
@@ -0,0 +1,305 @@
+//
+//  SKPDFSyncParser.m
+//  Skim
+//
+//  Created by Christiaan Hofman on 04/04/2024.
+/*
+ This software is Copyright (c) 2024
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "SKPDFSyncParser.h"
+#import "SKPDFSyncRecord.h"
+#import "NSPointerFunctions_SKExtensions.h"
+#import "NSScanner_SKExtensions.h"
+
+// Offset of coordinates in PDFKit and what pdfsync tells us. Don't know what 
they are; is this implementation dependent?
+static NSPoint pdfOffset = {0.0, 0.0};
+
+#define SKPDFSynchronizerPdfsyncExtension @"pdfsync"
+
+#define PDFSYNC_TO_PDF(coord) ((CGFloat)coord / 65536.0)
+
+@implementation SKPDFSyncParser
+
+@synthesize syncFileName;
+
+- (instancetype)initWithFileName:(NSString *)fileName {
+    self = [super init];
+    if (self) {
+        if ([[fileName pathExtension] 
isEqualToString:SKPDFSynchronizerPdfsyncExtension])
+            syncFileName = fileName;
+        else
+            syncFileName = [[fileName stringByDeletingPathExtension] 
stringByAppendingPathExtension:SKPDFSynchronizerPdfsyncExtension];
+        if ([[NSURL fileURLWithPath:syncFileName isDirectory:NO] 
checkResourceIsReachableAndReturnError:NULL]) {
+            pages = [[NSMutableArray alloc] init];
+            lines = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions strongPointerFunctions] capacity:0];
+            [self loadPdfsyncFile];
+        } else {
+            self = nil;
+        }
+    }
+    return self;
+}
+
+static inline NSString *removeQuotes(NSString *file) {
+    if ([file length] > 2 && [file characterAtIndex:0] == '"' && [file 
characterAtIndex:[file length] - 1] == '"')
+        return [file substringWithRange:NSMakeRange(1, [file length] - 2)];
+    return file;
+}
+
+static inline SKPDFSyncRecord *recordForIndex(NSMapTable *records, NSInteger 
recordIndex) {
+    SKPDFSyncRecord *record = (__bridge SKPDFSyncRecord *)NSMapGet(records, 
(void *)recordIndex);
+    if (record == nil) {
+        record = [[SKPDFSyncRecord alloc] initWithRecordIndex:recordIndex];
+        NSMapInsert(records, (void *)recordIndex, (__bridge void *)record);
+    }
+    return record;
+}
+
+- (void)loadPdfsyncFile {
+
+    NSString *pdfsyncString = [NSString stringWithContentsOfFile:syncFileName 
encoding:NSUTF8StringEncoding error:NULL];
+    
+    if ([pdfsyncString length]) {
+        
+        NSMapTable *records = [[NSMapTable alloc] 
initWithKeyOptions:NSPointerFunctionsOpaqueMemory | 
NSPointerFunctionsIntegerPersonality 
valueOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality capacity:0];
+        NSMutableArray *files = [[NSMutableArray alloc] init];
+        NSString *file;
+        NSInteger recordIndex, line, pageIndex;
+        double x, y;
+        SKPDFSyncRecord *record;
+        unichar ch;
+        NSScanner *sc = [[NSScanner alloc] initWithString:pdfsyncString];
+        NSCharacterSet *newlines = [NSCharacterSet newlineCharacterSet];
+        
+        [sc setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
+        
+        if ([sc scanUpToCharactersFromSet:newlines intoString:&file] &&
+            [sc scanCharactersFromSet:newlines intoString:NULL]) {
+            
+            file = removeQuotes(file);
+            [files addObject:file];
+            
+            [lines setObject:[NSMutableArray array] forKey:file];
+            
+            // we ignore the version
+            if ([sc scanString:@"version" intoString:NULL] && [sc 
scanInteger:NULL]) {
+                
+                [sc scanCharactersFromSet:newlines intoString:NULL];
+                
+                while ([sc scanCharacter:&ch]) {
+                    
+                    switch (ch) {
+                        case 'l':
+                            if ([sc scanInteger:&recordIndex] && [sc 
scanInteger:&line]) {
+                                // we ignore the column
+                                [sc scanInteger:NULL];
+                                record = recordForIndex(records, recordIndex);
+                                [record setFile:file];
+                                [record setLine:line];
+                                [[lines objectForKey:file] addObject:record];
+                            }
+                            break;
+                        case 'p':
+                            // we ignore * and + modifiers
+                            if ([sc scanString:@"*" intoString:NULL] == NO)
+                                [sc scanString:@"+" intoString:NULL];
+                            if ([sc scanInteger:&recordIndex] && [sc 
scanDouble:&x] && [sc scanDouble:&y]) {
+                                record = recordForIndex(records, recordIndex);
+                                [record setPageIndex:[pages count] - 1];
+                                [record setPoint:NSMakePoint(PDFSYNC_TO_PDF(x) 
+ pdfOffset.x, PDFSYNC_TO_PDF(y) + pdfOffset.y)];
+                                [[pages lastObject] addObject:record];
+                            }
+                            break;
+                        case 's':
+                            // start of a new page, the scanned integer should 
always equal [pages count]+1
+                            if ([sc scanInteger:&pageIndex] == NO) pageIndex = 
[pages count] + 1;
+                            while (pageIndex > (NSInteger)[pages count])
+                                [pages addObject:[NSMutableArray array]];
+                            break;
+                        case '(':
+                            // start of a new source file
+                            if ([sc scanUpToCharactersFromSet:newlines 
intoString:&file]) {
+                                file = removeQuotes(file);
+                                [files addObject:file];
+                                if ([lines objectForKey:file] == nil)
+                                    [lines setObject:[NSMutableArray array] 
forKey:file];
+                            }
+                            break;
+                        case ')':
+                            // closing of a source file
+                            if ([files count]) {
+                                [files removeLastObject];
+                                file = [files lastObject];
+                            }
+                            break;
+                        default:
+                            // shouldn't reach
+                            break;
+                    }
+                    
+                    [sc scanUpToCharactersFromSet:newlines intoString:NULL];
+                    [sc scanCharactersFromSet:newlines intoString:NULL];
+                }
+                
+                NSArray *lineSortDescriptors = @[[[NSSortDescriptor alloc] 
initWithKey:@"line" ascending:YES]];
+                NSArray *pointSortDescriptors = @[[[NSSortDescriptor alloc] 
initWithKey:@"x" ascending:YES], [[NSSortDescriptor alloc] initWithKey:@"y" 
ascending:NO]];
+                
+                for (NSMutableArray *array in [lines objectEnumerator])
+                    [array sortUsingDescriptors:lineSortDescriptors];
+                [pages 
makeObjectsPerformSelector:@selector(sortUsingDescriptors:)
+                                       withObject:pointSortDescriptors];
+                
+            }
+        }
+    }
+}
+
+- (void)enumerateSourceFilesUsingBlock:(void (^)(NSString *file))block {
+    for (NSString *file in lines)
+        block(file);
+}
+
+- (BOOL)findFileLine:(NSInteger *)linePtr file:(NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex {
+    BOOL rv = NO;
+    if (pageIndex < [pages count]) {
+        
+        SKPDFSyncRecord *record = nil;
+        SKPDFSyncRecord *beforeRecord = nil;
+        SKPDFSyncRecord *afterRecord = nil;
+        NSMutableDictionary *atRecords = [NSMutableDictionary dictionary];
+        
+        for (record in [pages objectAtIndex:pageIndex]) {
+            if ([record line] == 0)
+                continue;
+            NSPoint p = [record point];
+            if (p.y > NSMaxY(rect)) {
+                beforeRecord = record;
+            } else if (p.y < NSMinY(rect)) {
+                afterRecord = record;
+                break;
+            } else if (p.x < NSMinX(rect)) {
+                beforeRecord = record;
+            } else if (p.x > NSMaxX(rect)) {
+                afterRecord = record;
+                break;
+            } else {
+                [atRecords setObject:record forKey:[NSNumber 
numberWithDouble:fabs(p.x - point.x)]];
+            }
+        }
+        
+        record = nil;
+        if ([atRecords count]) {
+            NSNumber *nearest = [[[atRecords allKeys] 
sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:0];
+            record = [atRecords objectForKey:nearest];
+        } else if (beforeRecord && afterRecord) {
+            NSPoint beforePoint = [beforeRecord point];
+            NSPoint afterPoint = [afterRecord point];
+            if (beforePoint.y - point.y < point.y - afterPoint.y)
+                record = beforeRecord;
+            else if (beforePoint.y - point.y > point.y - afterPoint.y)
+                record = afterRecord;
+            else if (beforePoint.x - point.x < point.x - afterPoint.x)
+                record = beforeRecord;
+            else if (beforePoint.x - point.x > point.x - afterPoint.x)
+                record = afterRecord;
+            else
+                record = beforeRecord;
+        } else if (beforeRecord) {
+            record = beforeRecord;
+        } else if (afterRecord) {
+            record = afterRecord;
+        }
+        
+        if (record) {
+            *linePtr = [record line];
+            *filePtr = [record file];
+            rv = YES;
+        }
+    }
+    if (rv == NO)
+        NSLog(@"PDFSync was unable to find file and line.");
+    return rv;
+}
+
+- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file {
+    BOOL rv = NO;
+    NSArray *theLines = [lines objectForKey:file];
+    if (theLines) {
+        
+        SKPDFSyncRecord *record = nil;
+        SKPDFSyncRecord *beforeRecord = nil;
+        SKPDFSyncRecord *afterRecord = nil;
+        SKPDFSyncRecord *atRecord = nil;
+        
+        for (record in theLines) {
+            if ([record pageIndex] == NSNotFound)
+                continue;
+            NSInteger l = [record line];
+            if (l < line) {
+                beforeRecord = record;
+            } else if (l > line) {
+                afterRecord = record;
+                break;
+            } else {
+                atRecord = record;
+                break;
+            }
+        }
+        
+        if (atRecord) {
+            record = atRecord;
+        } else if (beforeRecord && afterRecord) {
+            NSInteger beforeLine = [beforeRecord line];
+            NSInteger afterLine = [afterRecord line];
+            if (beforeLine - line > line - afterLine)
+                record = afterRecord;
+            else
+                record = beforeRecord;
+        } else if (beforeRecord) {
+            record = beforeRecord;
+        } else if (afterRecord) {
+            record = afterRecord;
+        }
+        
+        if (record) {
+            *pageIndexPtr = [record pageIndex];
+            *pointPtr = [record point];
+            rv = YES;
+        }
+    }
+    if (rv == NO)
+        NSLog(@"PDFSync was unable to find location and page.");
+    return rv;
+}
+
+@end

Modified: trunk/SKPDFSynchronizer.h
===================================================================
--- trunk/SKPDFSynchronizer.h   2024-04-03 16:34:12 UTC (rev 14174)
+++ trunk/SKPDFSynchronizer.h   2024-04-04 16:12:03 UTC (rev 14175)
@@ -48,8 +48,7 @@
     SKPDFSynchronizerFlippedMask = 1 << 2,
 };
 
-@protocol SKPDFSynchronizerDelegate;
-@class SKSyncTeX, SKPDFSync;
+@protocol SKPDFSynchronizerDelegate, SKSyncParser;
 
 @interface SKPDFSynchronizer : NSObject {
     __weak id <SKPDFSynchronizerDelegate> delegate;
@@ -64,9 +63,11 @@
     
     NSFileManager *fileManager;
     
-    SKPDFSync *pdfsync;
-    SKSyncTeX *synctex;
+    NSMutableDictionary *sourceFileNames;
     
+    id<SKSyncParser> parser;
+    BOOL isPdfsync;
+    
     _Atomic(BOOL) shouldKeepRunning;
 }
 
@@ -74,7 +75,7 @@
 @property (nullable, copy) NSString *fileName;
 
 - (void)findFileAndLineForLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex;
-- (void)findPageAndLocationForLine:(NSInteger)line inFile:(NSString *)file 
options:(SKPDFSynchronizerOption)options;
+- (void)findPageAndLocationForLine:(NSInteger)line inFile:(nullable NSString 
*)file options:(SKPDFSynchronizerOption)options;
 
 // this must be called to stop the DO server from running in the server thread
 - (void)terminate;
@@ -81,6 +82,7 @@
 
 @end
 
+#pragma mark -
 
 @protocol SKPDFSynchronizerDelegate <NSObject>
 
@@ -89,4 +91,19 @@
 
 @end
 
+#pragma mark -
+
+@protocol SKSyncParser <NSObject>
+
+- (instancetype)initWithFileName:(NSString *)fileName;
+
+@property (nonatomic, nullable, readonly) NSString *syncFileName;
+
+- (void)enumerateSourceFilesUsingBlock:(void (^)(NSString *file))block;
+
+- (BOOL)findFileLine:(NSInteger *)linePtr file:(NSString * _Nullable 
__autoreleasing * _Nonnull)filePtr forLocation:(NSPoint)point 
inRect:(NSRect)rect pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex;
+- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(nullable NSString *)file;
+
+@end
+
 NS_ASSUME_NONNULL_END

Modified: trunk/SKPDFSynchronizer.m
===================================================================
--- trunk/SKPDFSynchronizer.m   2024-04-03 16:34:12 UTC (rev 14174)
+++ trunk/SKPDFSynchronizer.m   2024-04-04 16:12:03 UTC (rev 14175)
@@ -37,56 +37,15 @@
  */
 
 #import "SKPDFSynchronizer.h"
-#import "synctex_parser.h"
-#import "SKPDFSyncRecord.h"
+#import "SKSyncTeXParser.h"
+#import "SKPDFSyncParser.h"
 #import "NSCharacterSet_SKExtensions.h"
-#import "NSScanner_SKExtensions.h"
-#import <CoreFoundation/CoreFoundation.h>
 #import "NSFileManager_SKExtensions.h"
-#import "NSPointerFunctions_SKExtensions.h"
 
-#define PDFSYNC_TO_PDF(coord) ((CGFloat)coord / 65536.0)
-
-// Offset of coordinates in PDFKit and what pdfsync tells us. Don't know what 
they are; is this implementation dependent?
-static NSPoint pdfOffset = {0.0, 0.0};
-
-#define SKPDFSynchronizerPdfsyncExtension @"pdfsync"
 static NSArray *SKPDFSynchronizerTexExtensions = nil;
 
 #pragma mark -
 
-@interface SKSyncTeX : NSObject {
-    synctex_scanner_p scanner;
-    NSMapTable *filenames;
-}
-
-- (instancetype)initWithFileName:(NSString *)fileName;
-
-@property (nonatomic, readonly) synctex_scanner_p scanner 
NS_RETURNS_INNER_POINTER;
-@property (nonatomic, readonly) NSMapTable *filenames;
-
-- (BOOL)findFileLine:(NSInteger *)linePtr file:(out NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex;
-- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file;
-
-@end
-
-#pragma mark -
-
-@interface SKPDFSync : NSObject {
-    NSMutableArray<NSMutableArray<SKPDFSyncRecord *> *> *pages;
-    NSMapTable *lines;
-}
-
-@property (nonatomic, readonly) NSMutableArray<NSMutableArray<SKPDFSyncRecord 
*> *> *pages;
-@property (nonatomic, readonly) NSMapTable *lines;
-
-- (BOOL)findFileLine:(NSInteger *)linePtr file:(out NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex;
-- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file;
-
-@end
-
-#pragma mark -
-
 @implementation SKPDFSynchronizer
 
 @synthesize delegate;
@@ -108,8 +67,8 @@
         syncFileName = nil;
         lastModDate = nil;
         
-        pdfsync = nil;
-        synctex = nil;
+        parser = nil;
+        sourceFileNames = nil;
         
         shouldKeepRunning = YES;
         
@@ -178,9 +137,7 @@
 
 #pragma mark Support
 
-- (NSString *)sourceFileForFileName:(NSString *)file isTeX:(BOOL)isTeX 
removeQuotes:(BOOL)removeQuotes {
-    if (removeQuotes && [file length] > 2 && [file characterAtIndex:0] == '"' 
&& [file characterAtIndex:[file length] - 1] == '"')
-        file = [file substringWithRange:NSMakeRange(1, [file length] - 2)];
+- (NSString *)sourceFileForFileName:(NSString *)file isTeX:(BOOL)isTeX {
     if ([file isAbsolutePath] == NO)
         file = [[[self fileName] stringByDeletingLastPathComponent] 
stringByAppendingPathComponent:file];
     if (isTeX && [fileManager fileExistsAtPath:file] == NO && 
[SKPDFSynchronizerTexExtensions containsObject:[[file pathExtension] 
lowercaseString]] == NO) {
@@ -208,136 +165,22 @@
 
 #pragma mark Loading sync file
 
-static inline SKPDFSyncRecord *recordForIndex(NSMapTable *records, NSInteger 
recordIndex) {
-    SKPDFSyncRecord *record = (__bridge SKPDFSyncRecord *)NSMapGet(records, 
(void *)recordIndex);
-    if (record == nil) {
-        record = [[SKPDFSyncRecord alloc] initWithRecordIndex:recordIndex];
-        NSMapInsert(records, (void *)recordIndex, (__bridge void *)record);
-    }
-    return record;
-}
-
-- (BOOL)loadPdfsyncFile:(NSString *)theFileName {
-
-    pdfsync = [[SKPDFSync alloc] init];
-    synctex = nil;
-    
-    [self setSyncFileName:theFileName];
-    
-    NSString *pdfsyncString = [NSString stringWithContentsOfFile:theFileName 
encoding:NSUTF8StringEncoding error:NULL];
+- (BOOL)loadSyncFile:(BOOL)pdfSync forFileName:(NSString *)theFileName {
     BOOL rv = NO;
-    
-    if ([pdfsyncString length]) {
-        
-        NSMapTable *records = [[NSMapTable alloc] 
initWithKeyOptions:NSPointerFunctionsOpaqueMemory | 
NSPointerFunctionsIntegerPersonality 
valueOptions:NSPointerFunctionsStrongMemory | 
NSPointerFunctionsObjectPersonality capacity:0];
-        NSMutableArray *files = [[NSMutableArray alloc] init];
-        NSString *file;
-        NSInteger recordIndex, line, pageIndex;
-        double x, y;
-        SKPDFSyncRecord *record;
-        unichar ch;
-        NSScanner *sc = [[NSScanner alloc] initWithString:pdfsyncString];
-        NSCharacterSet *newlines = [NSCharacterSet newlineCharacterSet];
-        
-        [sc setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
-        
-        if ([sc scanUpToCharactersFromSet:newlines intoString:&file] &&
-            [sc scanCharactersFromSet:newlines intoString:NULL]) {
-            
-            file = [self sourceFileForFileName:file isTeX:YES 
removeQuotes:YES];
-            [files addObject:file];
-            
-            [[pdfsync lines] setObject:[NSMutableArray array] forKey:file];
-            
-            // we ignore the version
-            if ([sc scanString:@"version" intoString:NULL] && [sc 
scanInteger:NULL]) {
-                
-                [sc scanCharactersFromSet:newlines intoString:NULL];
-                
-                while (atomic_load(&shouldKeepRunning) && [sc 
scanCharacter:&ch]) {
-                    
-                    switch (ch) {
-                        case 'l':
-                            if ([sc scanInteger:&recordIndex] && [sc 
scanInteger:&line]) {
-                                // we ignore the column
-                                [sc scanInteger:NULL];
-                                record = recordForIndex(records, recordIndex);
-                                [record setFile:file];
-                                [record setLine:line];
-                                [[[pdfsync lines] objectForKey:file] 
addObject:record];
-                            }
-                            break;
-                        case 'p':
-                            // we ignore * and + modifiers
-                            if ([sc scanString:@"*" intoString:NULL] == NO)
-                                [sc scanString:@"+" intoString:NULL];
-                            if ([sc scanInteger:&recordIndex] && [sc 
scanDouble:&x] && [sc scanDouble:&y]) {
-                                record = recordForIndex(records, recordIndex);
-                                [record setPageIndex:[[pdfsync pages] count] - 
1];
-                                [record setPoint:NSMakePoint(PDFSYNC_TO_PDF(x) 
+ pdfOffset.x, PDFSYNC_TO_PDF(y) + pdfOffset.y)];
-                                [[[pdfsync pages] lastObject] 
addObject:record];
-                            }
-                            break;
-                        case 's':
-                            // start of a new page, the scanned integer should 
always equal [[pdfsync pages] count]+1
-                            if ([sc scanInteger:&pageIndex] == NO) pageIndex = 
[[pdfsync pages] count] + 1;
-                            while (pageIndex > (NSInteger)[[pdfsync pages] 
count])
-                                [[pdfsync pages] addObject:[NSMutableArray 
array]];
-                            break;
-                        case '(':
-                            // start of a new source file
-                            if ([sc scanUpToCharactersFromSet:newlines 
intoString:&file]) {
-                                file = [self sourceFileForFileName:file 
isTeX:YES removeQuotes:YES];
-                                [files addObject:file];
-                                if ([[pdfsync lines] objectForKey:file] == nil)
-                                    [[pdfsync lines] setObject:[NSMutableArray 
array] forKey:file];
-                            }
-                            break;
-                        case ')':
-                            // closing of a source file
-                            if ([files count]) {
-                                [files removeLastObject];
-                                file = [files lastObject];
-                            }
-                            break;
-                        default:
-                            // shouldn't reach
-                            break;
-                    }
-                    
-                    [sc scanUpToCharactersFromSet:newlines intoString:NULL];
-                    [sc scanCharactersFromSet:newlines intoString:NULL];
-                }
-                
-                NSArray *lineSortDescriptors = @[[[NSSortDescriptor alloc] 
initWithKey:@"line" ascending:YES]];
-                NSArray *pointSortDescriptors = @[[[NSSortDescriptor alloc] 
initWithKey:@"x" ascending:YES], [[NSSortDescriptor alloc] initWithKey:@"y" 
ascending:NO]];
-                
-                for (NSMutableArray *array in [[pdfsync lines] 
objectEnumerator])
-                    [array sortUsingDescriptors:lineSortDescriptors];
-                [[pdfsync pages] 
makeObjectsPerformSelector:@selector(sortUsingDescriptors:)
-                                       withObject:pointSortDescriptors];
-                
-                rv = atomic_load(&shouldKeepRunning);
-            }
-        }
-    }
-    
-    return rv;
-}
-
-- (BOOL)loadSynctexFileForFileName:(NSString *)theFileName {
-    BOOL rv = NO;
-    synctex = [[SKSyncTeX alloc] initWithFileName:theFileName];
-    if (synctex) {
-        const char *fileRep = synctex_scanner_get_synctex([synctex scanner]);
-        [self setSyncFileName:[self sourceFileForFileName:[NSString 
stringWithUTF8String:fileRep] isTeX:NO removeQuotes:NO]];
-        pdfsync = nil;
-        synctex_node_p node = synctex_scanner_input([synctex scanner]);
-        do {
-            if ((fileRep = synctex_scanner_get_name([synctex scanner], 
synctex_node_tag(node)))) {
-                NSMapInsert([synctex filenames], (__bridge void *)[self 
sourceFileForFileName:[NSString stringWithUTF8String:fileRep] isTeX:YES 
removeQuotes:NO], (void *)fileRep);
-            }
-        } while ((node = synctex_node_next(node)));
+    if (pdfSync)
+        parser = [[SKPDFSyncParser alloc] initWithFileName:theFileName];
+    else
+        parser = [[SKSyncTeXParser alloc] initWithFileName:theFileName];
+    if (parser) {
+        isPdfsync = pdfSync;
+        [self setSyncFileName:[self sourceFileForFileName:[parser 
syncFileName] isTeX:NO]];
+        if (sourceFileNames == nil)
+            sourceFileNames = [[NSMutableDictionary alloc] init];
+        else
+            [sourceFileNames removeAllObjects];
+        [parser enumerateSourceFilesUsingBlock:^(NSString *file){
+            [sourceFileNames setObject:[self sourceFileForFileName:file 
isTeX:YES] forKey:file];
+        }];
         rv = atomic_load(&shouldKeepRunning);
     }
     return rv;
@@ -357,17 +200,14 @@
         
             if (currentModDate && [modDate compare:currentModDate] != 
NSOrderedDescending)
                 rv = YES;
-            else if (pdfsync)
-                rv = [self loadPdfsyncFile:theSyncFileName];
-            else
-                rv = [self loadSynctexFileForFileName:theFileName];
+            else if (isPdfsync)
+                rv = [self loadSyncFile:YES forFileName:theSyncFileName];
+            if (rv == NO)
+                rv = [self loadSyncFile:NO forFileName:theFileName];
         } else {
-            rv = [self loadSynctexFileForFileName:theFileName];
-            if (rv == NO) {
-                theSyncFileName = [[theFileName stringByDeletingPathExtension] 
stringByAppendingPathExtension:SKPDFSynchronizerPdfsyncExtension];
-                if ([fileManager fileExistsAtPath:theSyncFileName])
-                    rv = [self loadPdfsyncFile:theSyncFileName];
-            }
+            rv = [self loadSyncFile:NO forFileName:theFileName];
+            if (rv == NO)
+                rv = [self loadSyncFile:YES forFileName:theFileName];
         }
     }
     if (rv == NO)
@@ -382,17 +222,10 @@
         if (atomic_load(&shouldKeepRunning) && [self loadSyncFileIfNeeded]) {
             NSInteger foundLine = 0;
             NSString *foundFile = nil;
-            BOOL success = NO;
+            BOOL success = [parser findFileLine:&foundLine file:&foundFile 
forLocation:point inRect:rect pageBounds:bounds atPageIndex:pageIndex];
             
-            if (pdfsync) {
-                success = [pdfsync findFileLine:&foundLine file:&foundFile 
forLocation:point inRect:rect pageBounds:bounds atPageIndex:pageIndex];
-            } else {
-                success = [synctex findFileLine:&foundLine file:&foundFile 
forLocation:point inRect:rect pageBounds:bounds atPageIndex:pageIndex];
-                if (success)
-                    foundFile = [self sourceFileForFileName:foundFile 
isTeX:YES removeQuotes:NO];
-            }
-            
             if (success && atomic_load(&shouldKeepRunning)) {
+                foundFile = [self sourceFileForFileName:foundFile isTeX:YES];
                 dispatch_async(dispatch_get_main_queue(), ^{
                     [delegate synchronizerFoundLine:foundLine 
inFile:foundFile];
                 });
@@ -410,15 +243,22 @@
             NSPoint foundPoint = NSZeroPoint;
             SKPDFSynchronizerOption foundOptions = options;
             BOOL success = NO;
-            NSString *fixedFile = [self sourceFileForFileName:file isTeX:YES 
removeQuotes:NO];
+            NSString *fixedFile = [self sourceFileForFileName:file isTeX:YES];
+            NSString *sourceFile = [sourceFileNames objectForKey:fixedFile] ?: 
[sourceFileNames objectForKey:[[fixedFile stringByResolvingSymlinksInPath] 
stringByStandardizingPath]];
+            if (sourceFile == nil) {
+                for (NSString *fn in sourceFileNames) {
+                    if ([[fn lastPathComponent] 
caseInsensitiveCompare:[fixedFile lastPathComponent]] == NSOrderedSame) {
+                        sourceFile = [sourceFileNames objectForKey:fixedFile];
+                        break;
+                    }
+                }
+                if (sourceFile == nil)
+                    sourceFile = [fixedFile lastPathComponent];
+            }
+            success = [parser findPage:&foundPageIndex location:&foundPoint 
forLine:line inFile:sourceFile];
             
-            if (pdfsync)
-                success = [pdfsync findPage:&foundPageIndex 
location:&foundPoint forLine:line inFile:fixedFile];
-            else
-                success = [synctex findPage:&foundPageIndex 
location:&foundPoint forLine:line inFile:fixedFile];
-            
             if (success && atomic_load(&shouldKeepRunning)) {
-                if (pdfsync)
+                if (isPdfsync)
                     foundOptions &= ~SKPDFSynchronizerFlippedMask;
                 else
                     foundOptions |= SKPDFSynchronizerFlippedMask;
@@ -431,206 +271,3 @@
 }
 
 @end
-
-#pragma mark -
-
-@implementation SKSyncTeX
-
-@synthesize scanner, filenames;
-
-- (instancetype)initWithFileName:(NSString *)fileName {
-    self = [super init];
-    if (self) {
-        scanner = synctex_scanner_new_with_output_file([fileName UTF8String], 
NULL, 1);
-        if (scanner) {
-            NSPointerFunctions *keyPointerFunctions = [NSPointerFunctions 
caseInsensitiveStringPointerFunctions];
-            NSPointerFunctions *valuePointerFunctions = [NSPointerFunctions 
pointerFunctionsWithOptions:NSPointerFunctionsMallocMemory | 
NSPointerFunctionsCStringPersonality | NSPointerFunctionsCopyIn];
-            filenames = [[NSMapTable alloc] 
initWithKeyPointerFunctions:keyPointerFunctions 
valuePointerFunctions:valuePointerFunctions capacity:0];
-        } else {
-            self = nil;
-        }
-    }
-    return self;
-}
-
-- (void)dealloc {
-    if (scanner) synctex_scanner_free(scanner);
-    scanner = NULL;
-}
-
-- (BOOL)findFileLine:(NSInteger *)linePtr file:(out NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex {
-    BOOL rv = NO;
-    if (synctex_edit_query(scanner, (int)pageIndex + 1, point.x, 
NSMaxY(bounds) - point.y) > 0) {
-        synctex_node_p node;
-        const char *file;
-        while (rv == NO && (node = synctex_scanner_next_result(scanner))) {
-            if ((file = synctex_scanner_get_name(scanner, 
synctex_node_tag(node)))) {
-                *linePtr = MAX(synctex_node_line(node), 1) - 1;
-                *filePtr = [NSString stringWithUTF8String:file];
-                rv = YES;
-            }
-        }
-    }
-    if (rv == NO)
-        NSLog(@"SyncTeX was unable to find file and line.");
-    return rv;
-}
-
-- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file {
-    BOOL rv = NO;
-    char *filename = (char *)NSMapGet(filenames, (__bridge void *)file) ?: 
(char *)NSMapGet(filenames, (__bridge void *)[[file 
stringByResolvingSymlinksInPath] stringByStandardizingPath]);
-    if (filename == NULL) {
-        for (NSString *fn in filenames) {
-            if ([[fn lastPathComponent] caseInsensitiveCompare:[file 
lastPathComponent]] == NSOrderedSame) {
-                filename = (char *)NSMapGet(filenames, (__bridge void *)file);
-                break;
-            }
-        }
-        if (filename == NULL)
-            filename = (char *)[[file lastPathComponent] UTF8String];
-    }
-    if (synctex_display_query(scanner, filename, (int)line + 1, 0, -1) > 0) {
-        synctex_node_p node = synctex_scanner_next_result(scanner);
-        if (node) {
-            NSUInteger page = synctex_node_page(node);
-            *pageIndexPtr = MAX(page, 1ul) - 1;
-            *pointPtr = NSMakePoint(synctex_node_visible_h(node), 
synctex_node_visible_v(node));
-            rv = YES;
-        }
-    }
-    if (rv == NO)
-        NSLog(@"SyncTeX was unable to find location and page.");
-    return rv;
-}
-
-@end
-
-#pragma mark -
-
-@implementation SKPDFSync
-
-@synthesize pages, lines;
-
-- (instancetype)init {
-    self = [super init];
-    if (self) {
-        pages = [[NSMutableArray alloc] init];
-        lines = [[NSMapTable alloc] 
initWithKeyPointerFunctions:[NSPointerFunctions 
caseInsensitiveStringPointerFunctions] 
valuePointerFunctions:[NSPointerFunctions strongPointerFunctions] capacity:0];
-    }
-    return self;
-}
-
-- (BOOL)findFileLine:(NSInteger *)linePtr file:(out NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex {
-    BOOL rv = NO;
-    if (pageIndex < [pages count]) {
-        
-        SKPDFSyncRecord *record = nil;
-        SKPDFSyncRecord *beforeRecord = nil;
-        SKPDFSyncRecord *afterRecord = nil;
-        NSMutableDictionary *atRecords = [NSMutableDictionary dictionary];
-        
-        for (record in [pages objectAtIndex:pageIndex]) {
-            if ([record line] == 0)
-                continue;
-            NSPoint p = [record point];
-            if (p.y > NSMaxY(rect)) {
-                beforeRecord = record;
-            } else if (p.y < NSMinY(rect)) {
-                afterRecord = record;
-                break;
-            } else if (p.x < NSMinX(rect)) {
-                beforeRecord = record;
-            } else if (p.x > NSMaxX(rect)) {
-                afterRecord = record;
-                break;
-            } else {
-                [atRecords setObject:record forKey:[NSNumber 
numberWithDouble:fabs(p.x - point.x)]];
-            }
-        }
-        
-        record = nil;
-        if ([atRecords count]) {
-            NSNumber *nearest = [[[atRecords allKeys] 
sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:0];
-            record = [atRecords objectForKey:nearest];
-        } else if (beforeRecord && afterRecord) {
-            NSPoint beforePoint = [beforeRecord point];
-            NSPoint afterPoint = [afterRecord point];
-            if (beforePoint.y - point.y < point.y - afterPoint.y)
-                record = beforeRecord;
-            else if (beforePoint.y - point.y > point.y - afterPoint.y)
-                record = afterRecord;
-            else if (beforePoint.x - point.x < point.x - afterPoint.x)
-                record = beforeRecord;
-            else if (beforePoint.x - point.x > point.x - afterPoint.x)
-                record = afterRecord;
-            else
-                record = beforeRecord;
-        } else if (beforeRecord) {
-            record = beforeRecord;
-        } else if (afterRecord) {
-            record = afterRecord;
-        }
-        
-        if (record) {
-            *linePtr = [record line];
-            *filePtr = [record file];
-            rv = YES;
-        }
-    }
-    if (rv == NO)
-        NSLog(@"PDFSync was unable to find file and line.");
-    return rv;
-}
-
-- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file {
-    BOOL rv = NO;
-    NSArray *theLines = [lines objectForKey:file];
-    if (theLines) {
-        
-        SKPDFSyncRecord *record = nil;
-        SKPDFSyncRecord *beforeRecord = nil;
-        SKPDFSyncRecord *afterRecord = nil;
-        SKPDFSyncRecord *atRecord = nil;
-        
-        for (record in theLines) {
-            if ([record pageIndex] == NSNotFound)
-                continue;
-            NSInteger l = [record line];
-            if (l < line) {
-                beforeRecord = record;
-            } else if (l > line) {
-                afterRecord = record;
-                break;
-            } else {
-                atRecord = record;
-                break;
-            }
-        }
-        
-        if (atRecord) {
-            record = atRecord;
-        } else if (beforeRecord && afterRecord) {
-            NSInteger beforeLine = [beforeRecord line];
-            NSInteger afterLine = [afterRecord line];
-            if (beforeLine - line > line - afterLine)
-                record = afterRecord;
-            else
-                record = beforeRecord;
-        } else if (beforeRecord) {
-            record = beforeRecord;
-        } else if (afterRecord) {
-            record = afterRecord;
-        }
-        
-        if (record) {
-            *pageIndexPtr = [record pageIndex];
-            *pointPtr = [record point];
-            rv = YES;
-        }
-    }
-    if (rv == NO)
-        NSLog(@"PDFSync was unable to find location and page.");
-    return rv;
-}
-
-@end

Added: trunk/SKSyncTeXParser.h
===================================================================
--- trunk/SKSyncTeXParser.h                             (rev 0)
+++ trunk/SKSyncTeXParser.h     2024-04-04 16:12:03 UTC (rev 14175)
@@ -0,0 +1,51 @@
+//
+//  SKSyncTeXParser.h
+//  Skim
+//
+//  Created by Christiaan Hofman on 04/04/2024.
+/*
+ This software is Copyright (c) 2024
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "SKPDFSynchronizer.h"
+#import "synctex_parser.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SKSyncTeXParser : NSObject <SKSyncParser> {
+    synctex_scanner_p scanner;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END

Added: trunk/SKSyncTeXParser.m
===================================================================
--- trunk/SKSyncTeXParser.m                             (rev 0)
+++ trunk/SKSyncTeXParser.m     2024-04-04 16:12:03 UTC (rev 14175)
@@ -0,0 +1,112 @@
+//
+//  SKSyncTeXParser.m
+//  Skim
+//
+//  Created by Christiaan Hofman on 04/04/2024.
+/*
+ This software is Copyright (c) 2024
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "SKSyncTeXParser.h"
+
+
+@implementation SKSyncTeXParser
+
+@dynamic syncFileName;
+
+- (instancetype)initWithFileName:(NSString *)fileName {
+    self = [super init];
+    if (self) {
+        scanner = synctex_scanner_new_with_output_file([fileName UTF8String], 
NULL, 1);
+        if (scanner == NULL) {
+            self = nil;
+        }
+    }
+    return self;
+}
+
+- (void)dealloc {
+    if (scanner) synctex_scanner_free(scanner);
+    scanner = NULL;
+}
+
+- (NSString *)syncFileName {
+    const char *fileRep = synctex_scanner_get_synctex(scanner);
+    return [NSString stringWithUTF8String:fileRep];
+}
+
+- (void)enumerateSourceFilesUsingBlock:(void (^)(NSString *file))block {
+    const char *fileRep;
+    synctex_node_p node = synctex_scanner_input(scanner);
+    do {
+        if ((fileRep = synctex_scanner_get_name(scanner, 
synctex_node_tag(node)))) {
+            block([NSString stringWithUTF8String:fileRep]);
+        }
+    } while ((node = synctex_node_next(node)));
+}
+
+- (BOOL)findFileLine:(NSInteger *)linePtr file:(NSString * __autoreleasing 
*)filePtr forLocation:(NSPoint)point inRect:(NSRect)rect 
pageBounds:(NSRect)bounds atPageIndex:(NSUInteger)pageIndex {
+    BOOL rv = NO;
+    if (synctex_edit_query(scanner, (int)pageIndex + 1, point.x, 
NSMaxY(bounds) - point.y) > 0) {
+        synctex_node_p node;
+        const char *file;
+        while (rv == NO && (node = synctex_scanner_next_result(scanner))) {
+            if ((file = synctex_scanner_get_name(scanner, 
synctex_node_tag(node)))) {
+                *linePtr = MAX(synctex_node_line(node), 1) - 1;
+                *filePtr = [NSString stringWithUTF8String:file];
+                rv = YES;
+            }
+        }
+    }
+    if (rv == NO)
+        NSLog(@"SyncTeX was unable to find file and line.");
+    return rv;
+}
+
+- (BOOL)findPage:(NSUInteger *)pageIndexPtr location:(NSPoint *)pointPtr 
forLine:(NSInteger)line inFile:(NSString *)file {
+    BOOL rv = NO;
+    const char *filename = [file UTF8String];
+    if (synctex_display_query(scanner, filename, (int)line + 1, 0, -1) > 0) {
+        synctex_node_p node = synctex_scanner_next_result(scanner);
+        if (node) {
+            NSUInteger page = synctex_node_page(node);
+            *pageIndexPtr = MAX(page, 1ul) - 1;
+            *pointPtr = NSMakePoint(synctex_node_visible_h(node), 
synctex_node_visible_v(node));
+            rv = YES;
+        }
+    }
+    if (rv == NO)
+        NSLog(@"SyncTeX was unable to find location and page.");
+    return rv;
+}
+
+@end

Modified: trunk/Skim.xcodeproj/project.pbxproj
===================================================================
--- trunk/Skim.xcodeproj/project.pbxproj        2024-04-03 16:34:12 UTC (rev 
14174)
+++ trunk/Skim.xcodeproj/project.pbxproj        2024-04-04 16:12:03 UTC (rev 
14175)
@@ -237,6 +237,8 @@
                CEBC0DDD2791C607008686E8 /* NSObject_SKExtensions.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = CEBC0DDC2791C607008686E8 /* 
NSObject_SKExtensions.m */; };
                CEBCA4C02868A93A00E6376E /* SKLine.m in Sources */ = {isa = 
PBXBuildFile; fileRef = CEBCA4BF2868A93A00E6376E /* SKLine.m */; };
                CEBD52ED0C9C0AE500FBF6A4 /* SKBookmark.m in Sources */ = {isa = 
PBXBuildFile; fileRef = CEBD52EB0C9C0AE500FBF6A4 /* SKBookmark.m */; };
+               CEC1B0432BBF00D8006786B0 /* SKSyncTeXParser.m in Sources */ = 
{isa = PBXBuildFile; fileRef = CEC1B0422BBF00D8006786B0 /* SKSyncTeXParser.m 
*/; };
+               CEC1B0602BBF03AB006786B0 /* SKPDFSyncParser.m in Sources */ = 
{isa = PBXBuildFile; fileRef = CEC1B05F2BBF03AB006786B0 /* SKPDFSyncParser.m 
*/; };
                CEC29533275A66A2000F2D4C /* SKNotePrefs.m in Sources */ = {isa 
= PBXBuildFile; fileRef = CEC29532275A66A2000F2D4C /* SKNotePrefs.m */; };
                CEC29536275A7D58000F2D4C /* SKPreferencesCommand.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = CEC29535275A7D58000F2D4C /* 
SKPreferencesCommand.m */; };
                CEC3AD240E23EC0300F40B0B /* PDFAnnotationLink_SKExtensions.m in 
Sources */ = {isa = PBXBuildFile; fileRef = CEC3AD230E23EC0300F40B0B /* 
PDFAnnotationLink_SKExtensions.m */; };
@@ -1151,6 +1153,10 @@
                CEBCA4BF2868A93A00E6376E /* SKLine.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SKLine.m; 
sourceTree = "<group>"; };
                CEBD52EA0C9C0AE500FBF6A4 /* SKBookmark.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
SKBookmark.h; sourceTree = "<group>"; };
                CEBD52EB0C9C0AE500FBF6A4 /* SKBookmark.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= SKBookmark.m; sourceTree = "<group>"; };
+               CEC1B0412BBF00D8006786B0 /* SKSyncTeXParser.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKSyncTeXParser.h; 
sourceTree = "<group>"; };
+               CEC1B0422BBF00D8006786B0 /* SKSyncTeXParser.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
SKSyncTeXParser.m; sourceTree = "<group>"; };
+               CEC1B05E2BBF03AB006786B0 /* SKPDFSyncParser.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKPDFSyncParser.h; 
sourceTree = "<group>"; };
+               CEC1B05F2BBF03AB006786B0 /* SKPDFSyncParser.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
SKPDFSyncParser.m; sourceTree = "<group>"; };
                CEC29531275A66A2000F2D4C /* SKNotePrefs.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKNotePrefs.h; 
sourceTree = "<group>"; };
                CEC29532275A66A2000F2D4C /* SKNotePrefs.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SKNotePrefs.m; 
sourceTree = "<group>"; };
                CEC29534275A7D58000F2D4C /* SKPreferencesCommand.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
SKPreferencesCommand.h; sourceTree = "<group>"; };
@@ -1990,6 +1996,10 @@
                                CE5FA1660C909886008BE480 /* SKFDFParser.m */,
                                CE1E2B260BDAB6180011D9DD /* SKPDFSynchronizer.h 
*/,
                                CE1E2B270BDAB6180011D9DD /* SKPDFSynchronizer.m 
*/,
+                               CEC1B0412BBF00D8006786B0 /* SKSyncTeXParser.h 
*/,
+                               CEC1B0422BBF00D8006786B0 /* SKSyncTeXParser.m 
*/,
+                               CEC1B05E2BBF03AB006786B0 /* SKPDFSyncParser.h 
*/,
+                               CEC1B05F2BBF03AB006786B0 /* SKPDFSyncParser.m 
*/,
                                CE48BAD50C089EA300A166C6 /* SKTemplateParser.h 
*/,
                                CE48BAD60C089EA300A166C6 /* SKTemplateParser.m 
*/,
                        );
@@ -2800,6 +2810,7 @@
                                CEFDF8BE0B3848C40091C61A /* 
SKNavigationWindow.m in Sources */,
                                45A3BD080B4F0770002B297F /* SKStringConstants.m 
in Sources */,
                                CE3A3D2C0B78C0A0006B64D3 /* 
SKNPDFAnnotationNote_SKExtensions.m in Sources */,
+                               CEC1B0602BBF03AB006786B0 /* SKPDFSyncParser.m 
in Sources */,
                                CE3A41580B790C56006B64D3 /* SKDragImageView.m 
in Sources */,
                                CE3A45530B7A04A4006B64D3 /* 
NSBezierPath_SKExtensions.m in Sources */,
                                CEBCA4C02868A93A00E6376E /* SKLine.m in Sources 
*/,
@@ -2959,6 +2970,7 @@
                                CE4E1E9A12BCFF5A005227ED /* HIDRemote.m in 
Sources */,
                                CE171E3512C3AC1600291179 /* 
SKFileUpdateChecker.m in Sources */,
                                CEECD61C12E9E30B00B9E35E /* 
NSError_SKExtensions.m in Sources */,
+                               CEC1B0432BBF00D8006786B0 /* SKSyncTeXParser.m 
in Sources */,
                                CE12A73E134F215F003AED67 /* SKSelectCommand.m 
in Sources */,
                                CE030C6113C07A57007A47E9 /* 
PDFView_SKExtensions.m in Sources */,
                                CEB402C313EDAD6100851D1B /* SKTemporaryData.m 
in Sources */,

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit

Reply via email to