Hello!

I'm maintaining system-wide Latvian spell checker for ~ 2 years. It works as intended on 10.4 and 10.5. Just made a dummy spellcheck for 10.6. checking out the new SDK. Works great... ...except for the (only) new "Automatic by Language" option introduced in 10.6.

Problem description (pardon if failed to keep it shorter) :
After installing (see Compiled service structure) the spell check, opening i.e. TextEdit, I am able to check spelling for the registered (here- Latvian) language by setting "Latvian" in "Spelling and Grammar". The words that are not in the language (in wordsAray) are underlined, right-clicking them gives me a list of possible corrections (suggestion1, suggestion2,...), pressing esc gives the possible completions. I mean - everything works. As I switch to "Automatic by language" in "Spelling and Grammar" a weird behaviour occurs - it doesn't work as intended.

Text written in English, German a.o. Apple supported languages is correctly checked. Text written in Latvian is ... underlined as incorrect. However, when right-clicking these underlined words or invoking completion I get the suggestions that are delivered by my FooSpellCheck, that is, NSSpellServer is calling methods - (NSArray *)spellServer:(NSSpellServer *)sender suggestCompletionsForPartialWordRange:(NSRange)range inString: (NSString *)string language:(NSString *)language; - (NSArray *)spellServer:(NSSpellServer *)sender suggestGuessesForWord: (NSString *)word inLanguage:(NSString *)language;
to my spellcheck!, however methods
// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender checkString:(NSString *)stringToCheck offset:(NSUInteger)offset types:(NSTextCheckingTypes) checkingTypes options:(NSDictionary *)options orthography: (NSOrthography *)orthography wordCount:(NSInteger *)wordCount;
// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender findMisspelledWordInString:(NSString *)stringToCheck language: (NSString *)language wordCount:(NSInteger *)wordCount countOnly:(BOOL) countOnly;
aren't! THEY SHOULD! Simply NSLog'ing the runtime shows this clearly.
Note: Yes, in sysprefs language&text everything is set/checked as needed under Automatic by Language setup.

• So is this this a bug in Foundation frameworks NSSpellServer?
• Or is it just me?
• Or automatic by language is reserved for apple provided languages only? (If so, they have bug in system preferences, as FooSpellCheck provided Latvian language shows up there and user is able to select it as one of langages for "automatic"). • If only New method is implemented then rightclicking misspelled word, when spelling options set to "Latvian only", doesn't call - spellServer:suggestGuessesForWord:inLanguage:language, it is called ONLY when checking spelling through Spelling and Grammar panel (cmd + : ) For OSX built-in languages right-clicking invokes list of possible corrections. However, when "automatic" is selected, then list is called. D'oh?

As I am going blind in this problem, tried things like changing registered language id (lv, lv_LV, Latvian, Latviešu). I have tried to run FooSpellCheck in 3 ways: Implementing both or only the new or only the old method for misspeled word search
// New method: Search for a misspelled word in a given string
// Old method: Search for a misspelled word in a given string
Have changed the vendor id and NSSpellChecker in Info.plist, a.o. "shots in the dark".

Before putting out spellcheck for 10.6. I'd really liked to solve this issue, or if this cannot be solved :) then I can clearly comment this problem in my spellchecks documentation.

Any suggestions/directions?

Many thanks in advance,
Reinis Adovics


Code for foo spellchecker:

////////////////////////
// FooSpellCheck.h
////////////////////////

#import <Foundation/Foundation.h>
#import <Foundation/NSSpellServer.h>

@interface FooSpellCheckClass : NSObject <NSSpellServerDelegate> {
        NSArray *wordsAray;
}
- (id <NSSpellServerDelegate>)init;
- (void) dealloc;
// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender checkString:(NSString *)stringToCheck offset:(NSUInteger)offset types:(NSTextCheckingTypes) checkingTypes options:(NSDictionary *)options orthography: (NSOrthography *)orthography wordCount:(NSInteger *)wordCount;
// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender findMisspelledWordInString:(NSString *)stringToCheck language: (NSString *)language wordCount:(NSInteger *)wordCount countOnly:(BOOL) countOnly; - (NSArray *)spellServer:(NSSpellServer *)sender suggestCompletionsForPartialWordRange:(NSRange)range inString: (NSString *)string language:(NSString *)language; - (NSArray *)spellServer:(NSSpellServer *)sender suggestGuessesForWord: (NSString *)word inLanguage:(NSString *)language; - (void)spellServer:(NSSpellServer *)sender didLearnWord:(NSString *) word inLanguage:(NSString *)language; - (void)spellServer:(NSSpellServer *)sender didForgetWord:(NSString *) word inLanguage:(NSString *)language;
- (BOOL)isWordCorrect:(NSString *)word;
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString: (NSString *)string language:(NSString *)language details:(NSArray **) outDetails;
@end

////////////////////////
// FooSpellCheck.m
////////////////////////

#import "FooSpellCheck.h"

@implementation FooSpellCheckClass

- (id <NSSpellServerDelegate>)init
{
        self = [super init];
    if ( self ) {
                // set up array containing all correct Latvian words, to check 
against
wordsAray = [NSArray arrayWithObjects :@"labdien ",@"kaut",@"kad",@"nezinu",@"nepareizs",@"draugs",@"interese",nil];
    }
    return self;
}

- (void) dealloc
{
    [super dealloc];
}

// New method: Search for a misspelled word in a given string
- (NSArray *)spellServer:(NSSpellServer *)sender
                         checkString:(NSString *)stringToCheck
                                  offset:(NSUInteger)offset
                                   types:(NSTextCheckingTypes)checkingTypes
                                 options:(NSDictionary *)options
                         orthography:(NSOrthography *)orthography
                           wordCount:(NSInteger *)wordCount
{
NSLog(@"FooSpellCheck. New (10.6. method) called. String to check: %@ \n",stringToCheck); NSScanner *stringToCheckScanner = [NSScanner scannerWithString:stringToCheck]; // create NSScanner object to scan stringToCheck NSCharacterSet *wordCharSet = [NSCharacterSet alphanumericCharacterSet]; // set allowed charsets for words - letters and numbers *wordCount = [[stringToCheck componentsSeparatedByString:@" "] count]; // get number of words in stringToCheck NSMutableArray *returnArray = [NSMutableArray array]; // create return array
        while (![stringToCheckScanner isAtEnd]) // while scanner is not at end
        {
[stringToCheckScanner scanUpToCharactersFromSet:wordCharSet intoString:nil]; // scans the string until a character from wordCharSet character set is encountered, send accumulating characters into nil if (![stringToCheckScanner isAtEnd]) // if scanner at this point is not at end (or characters from the set to be skipped remaining != TRUE), we have found a word
                {
                        NSString *wordToCheck; //create string object for word
[stringToCheckScanner scanCharactersFromSet:wordCharSet intoString:&wordToCheck]; // scan the stringToCheck as long as characters from wordCharSet are encountered
                        // and accumulate characters into wordToCheck
// if word is in dictionary (wordsAray) or or word is in user dictionary if ([self isWordCorrect:wordToCheck] || ([sender isWordInUserDictionaries:wordToCheck caseSensitive:YES]))
                        {
                                continue;
                        }
                        else
                        {
[returnArray addObject:[NSTextCheckingResult spellCheckingResultWithRange:NSMakeRange(offset + [stringToCheckScanner scanLocation] - [wordToCheck length], [wordToCheck length])]];
                        }
                }
        }
        return returnArray;
}


// Old method: Search for a misspelled word in a given string
- (NSRange)spellServer:(NSSpellServer *)sender findMisspelledWordInString:(NSString *)stringToCheck language: (NSString *)language wordCount:(NSInteger *)wordCount countOnly:(BOOL) countOnly
{
NSLog(@"FooSpellCheck. Old (10.4.,5. method) called. String to check: %...@\n",stringToCheck); NSScanner *stringToCheckScanner = [NSScanner scannerWithString:stringToCheck]; // create NSScanner object to scan stringToCheck NSCharacterSet *wordCharSet = [NSCharacterSet alphanumericCharacterSet]; // set allowed charsets for words - letters and numbers
        if (!countOnly) {  // if !countOnly, then we check spelling
                while (![stringToCheckScanner isAtEnd]) // while scanner is not 
at end
                {
[stringToCheckScanner scanUpToCharactersFromSet:wordCharSet intoString:nil]; // scans the string until a character from wordCharSet character set is encountered, send accumulating characters into nil if (![stringToCheckScanner isAtEnd]) // if scanner at this point is not at end (or characters from the set to be skipped remaining != TRUE), we have found a word
                        {
                                NSString *wordToCheck; //create string object 
for word
[stringToCheckScanner scanCharactersFromSet:wordCharSet intoString:&wordToCheck]; // scan the stringToCheck as long as characters from wordCharSet are encountered
                                // and accumulate characters into wordToCheck
// if word is in dictionary (wordsAray) or or word is in user dictionary if ([self isWordCorrect:wordToCheck] || ([sender isWordInUserDictionaries:wordToCheck caseSensitive:YES]))
                                {
                                        continue;
                                }
                                else
                                {
return NSMakeRange ([stringToCheckScanner scanLocation] - [wordToCheck length], [wordToCheck length]);
                                }
                        }
                }
        }
        else
        { // else we count only the words in the string object
if (wordCount) *wordCount = [[stringToCheck componentsSeparatedByString:@" "] count]; // get number of words in stringToCheck
        }
return NSMakeRange (NSNotFound, 0); // if our scanner failed to return range, then simply return {0,0} range
}


// Possible word completions, based on a partially completed string
- (NSArray *)spellServer:(NSSpellServer *)sender suggestCompletionsForPartialWordRange:(NSRange)range inString: (NSString *)string language:(NSString *)language
{
NSLog(@"FooSpellCheck. Word completions asked for: %...@\n",[string substringWithRange:range]);
        // Return a simple array
return [NSArray arrayWithObjects:@"completion1", @"completion2", @"completion3", @"completion3", nil];
}

// Suggest guesses for the correct spelling of the given misspelled word
- (NSArray *)spellServer:(NSSpellServer *)sender suggestGuessesForWord: (NSString *)word inLanguage:(NSString *)language
{
        NSLog(@"FooSpellCheck. Word suggestions asked for: %...@\n",word);
        // Return a simple array
return [NSArray arrayWithObjects:@"suggestion1", @"suggestion2", @"suggestion3", @"suggestion4", nil];
}


// User has added the specified word to the user’s list of acceptable words in the specified language. - (void)spellServer:(NSSpellServer *)sender didLearnWord:(NSString *) word inLanguage:(NSString *)language
{
        // do nothing
}

// User has removed the specified word from the user’s list of acceptable words in the specified language - (void)spellServer:(NSSpellServer *)sender didForgetWord:(NSString *) word inLanguage:(NSString *)language
{
        // do nothing
}

// Search for a grammar in a given string
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString: (NSString *)string language:(NSString *)language details:(NSArray **) outDetails
{
        // no gramatical issues found
        NSArray* myGrammaticalIssues = [NSArray array];
        *outDetails = myGrammaticalIssues;
        return NSMakeRange (NSNotFound, 0);
}

- (BOOL)isWordCorrect:(NSString *)word
{
        for(unsigned int i=0;i<[wordsAray count];i++) {
if ([[wordsAray objectAtIndex:i] caseInsensitiveCompare:word] == (NSComparisonResult)NSOrderedSame)
                        return TRUE;            
        }
        return FALSE;
}

@end

////////////////////////
// main.m
////////////////////////

#import <Foundation/Foundation.h>
#import "FooSpellCheck.h"

int main()
{
        NSAutoreleasePool *autoreleasepool= [[NSAutoreleasePool alloc] init];
NSSpellServer *mySpellServer = [[[NSSpellServer alloc] init] autorelease];
        NSLog(@"New NSSpellServer instance starting.\n");
if ([mySpellServer registerLanguage:@"lv" byVendor:@"Apple"]) // Vendor "Whatever" can be used, NSSpellChecker in info plist has to be the same as vendor
        {
                NSLog(@"Latvian language registred.\n");
[mySpellServer setDelegate:[[[FooSpellCheckClass alloc] init] autorelease]];
                NSLog(@"Spell server delegate FooSpellCheck allocated.\n");
                [mySpellServer run];
                fprintf(stderr, "Unexpected death of spellchecker 
FooSpellCheck!\n");
        } else {
fprintf(stderr, "NSSpellServer unable to register Latvian language. \n");
        }
        [autoreleasepool release];
        return 0;
}


////////////////////////
// Info.plist
////////////////////////

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd ">
<plist version="1.0">
<dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
        <string>FooSpellCheck</string>
        <key>CFBundleGetInfoString</key>
        <string>FooSpellCheck</string>
        <key>CFBundleIconFile</key>
        <string>FooSpellCheck.icns</string>
        <key>CFBundleIdentifier</key>
        <string>my.company.FooSpellCheck</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>FooSpellCheck</string>
        <key>CFBundlePackageType</key>
        <string>BNDL</string>
        <key>CFBundleShortVersionString</key>
        <string>3.0</string>
        <key>CFBundleSignature</key>
        <string>krko</string>
        <key>CFBundleVersion</key>
        <string>3.0</string>
        <key>LSBackgroundOnly</key>
        <true/>
        <key>NSPrincipalClass</key>
        <string>FooSpellCheckClass</string>
        <key>NSServices</key>
        <array>
                <dict>
                        <key>NSExecutable</key>
                        <string>FooSpellCheck</string>
                        <key>NSLanguages</key>
                        <array>
                                <string>lv</string>
                        </array>
                        <key>NSPortName</key>
                        <string>FooSpellCheck</string>
                        <key>NSSpellChecker</key>
                        <string>Apple</string>
                </dict>
        </array>
</dict>
</plist>

Compiled service structure:
Compiled using only 10.6 SDK, target os 10.6, Intel 32/64bit
Located at /Library/Services/ (chown root:wheel)
FooSpellCheck.service
        |
        Contents
                |
                Info.plist
                MacOS
                        |
                        FooSpellCheck

restarting lsregister with "lsregister -kill -r -f -domain system - domain local -domain user"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to