Mercurial > vim
view src/os_macosx.m @ 32721:94f4a488412e v9.0.1683
Updated runtime files
Commit: https://github.com/vim/vim/commit/6efb1980336ff324e9c57a4e282530b952fca816
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Aug 10 05:44:25 2023 +0200
Updated runtime files
This is a collection of various PRs from github that all require a minor
patch number:
1) https://github.com/vim/vim/pull/12612
Do not conflate dictionary key with end of block
2) https://github.com/vim/vim/pull/12729:
When saving and restoring 'undolevels', the constructs `&undolevels` and
`:set undolevels` are problematic.
The construct `&undolevels` reads an unpredictable value; it will be the
local option value (if one has been set), or the global option value
(otherwise), making it unsuitable for saving a value for later
restoration.
Similarly, if a local option value has been set for 'undolevels',
temporarily modifying the option via `:set undolevels` changes the local
value as well as the global value, requiring extra work to restore both
values.
Saving and restoring the option value in one step via the construct
`:let &undolevels = &undolevels` appears to make no changes to the
'undolevels' option, but if a local option has been set to a different
value than the global option, it has the unintended effect of changing
the global 'undolevels' value to the local value.
Update the documentation to explain these issues and recommend explicit
use of global and local option values when saving and restoring. Update
some unit tests to use `g:undolevels`.
3) https://github.com/vim/vim/pull/12702:
Problem: Pip requirements files are not recognized.
Solution: Add a pattern to match pip requirements files.
4) https://github.com/vim/vim/pull/12688:
Add indent file and tests for ABB Rapid
5) https://github.com/vim/vim/pull/12668:
Use Lua 5.1 numeric escapes in tests and add to CI
Only Lua 5.2+ and LuaJIT understand hexadecimal escapes in strings. Lua
5.1 only supports decimal escapes:
> A character in a string can also be specified by its numerical value
> using the escape sequence \ddd, where ddd is a sequence of up to three
> decimal digits. (Note that if a numerical escape is to be followed by a
> digit, it must be expressed using exactly three digits.) Strings in Lua
> can contain any 8-bit value, including embedded zeros, which can be
> specified as '\0'.
To make sure this works with Lua 5.4 and Lua 5.1 change the Vim CI to
run with Lua 5.1 as well as Lua 5.4
6) https://github.com/vim/vim/pull/12631:
Add hurl filetype detection
7) https://github.com/vim/vim/pull/12573:
Problem: Files for haskell persistent library are not recognized
Solution: Add pattern persistentmodels for haskell persistent library
closes: #12612
closes: #12729
closes: #12702
closes: #12688
closes: #12668
closes: #12631
closes: #12573
Co-authored-by: lacygoill <lacygoill@lacygoill.me>
Co-authored-by: Michael Henry <drmikehenry@drmikehenry.com>
Co-authored-by: ObserverOfTime <chronobserver@disroot.org>
Co-authored-by: KnoP-01 <knosowski@graeffrobotics.de>
Co-authored-by: James McCoy <jamessan@jamessan.com>
Co-authored-by: Jacob Pfeifer <jacob@pfeifer.dev>
Co-authored-by: Borys Lykah <lykahb@fastmail.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 10 Aug 2023 06:30:06 +0200 |
parents | ca4e81de477a |
children |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * os_macosx.m -- Mac specific things for Mac OS X. */ /* Suppress compiler warnings to non-C89 code. */ #if defined(__clang__) && defined(__STRICT_ANSI__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeclaration-after-statement" #endif /* Avoid a conflict for the definition of Boolean between Mac header files and * X11 header files. */ #define NO_X11_INCLUDES #include <stdbool.h> #include <mach/boolean.h> #include <sys/errno.h> #include <stdlib.h> #ifdef FEAT_RELTIME #include <dispatch/dispatch.h> #endif #include "vim.h" #import <AppKit/AppKit.h> /* * Clipboard support for the console. */ #if defined(FEAT_CLIPBOARD) /* Used to identify clipboard data copied from Vim. */ NSString *VimPboardType = @"VimPboardType"; void clip_mch_lose_selection(Clipboard_T *cbd UNUSED) { } int clip_mch_own_selection(Clipboard_T *cbd UNUSED) { /* This is called whenever there is a new selection and 'guioptions' * contains the "a" flag (automatically copy selection). Return TRUE, else * the "a" flag does nothing. Note that there is no concept of "ownership" * of the clipboard in Mac OS X. */ return TRUE; } void clip_mch_request_selection(Clipboard_T *cbd) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSPasteboard *pb = [NSPasteboard generalPasteboard]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType, NSPasteboardTypeString, nil]; #else NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType, NSStringPboardType, nil]; #endif NSString *bestType = [pb availableTypeFromArray:supportedTypes]; if (!bestType) goto releasepool; int motion_type = MAUTO; NSString *string = nil; if ([bestType isEqual:VimPboardType]) { /* This type should consist of an array with two objects: * 1. motion type (NSNumber) * 2. text (NSString) * If this is not the case we fall back on using NSPasteboardTypeString. */ id plist = [pb propertyListForType:VimPboardType]; if ([plist isKindOfClass:[NSArray class]] && [plist count] == 2) { id obj = [plist objectAtIndex:1]; if ([obj isKindOfClass:[NSString class]]) { motion_type = [[plist objectAtIndex:0] intValue]; string = obj; } } } if (!string) { /* Use NSPasteboardTypeString. The motion type is detected automatically. */ #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 NSMutableString *mstring = [[pb stringForType:NSPasteboardTypeString] mutableCopy]; #else NSMutableString *mstring = [[pb stringForType:NSStringPboardType] mutableCopy]; #endif if (!mstring) goto releasepool; /* Replace unrecognized end-of-line sequences with \x0a (line feed). */ NSRange range = { 0, [mstring length] }; unsigned n = [mstring replaceOccurrencesOfString:@"\x0d\x0a" withString:@"\x0a" options:0 range:range]; if (0 == n) { n = [mstring replaceOccurrencesOfString:@"\x0d" withString:@"\x0a" options:0 range:range]; } string = mstring; } /* Default to MAUTO, uses MCHAR or MLINE depending on trailing NL. */ if (!(MCHAR == motion_type || MLINE == motion_type || MBLOCK == motion_type || MAUTO == motion_type)) motion_type = MAUTO; char_u *str = (char_u*)[string UTF8String]; int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; if (input_conv.vc_type != CONV_NONE) str = string_convert(&input_conv, str, &len); if (str) clip_yank_selection(motion_type, str, len, cbd); if (input_conv.vc_type != CONV_NONE) vim_free(str); releasepool: [pool release]; } /* * Send the current selection to the clipboard. */ void clip_mch_set_selection(Clipboard_T *cbd) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; /* If the '*' register isn't already filled in, fill it in now. */ cbd->owned = TRUE; clip_get_selection(cbd); cbd->owned = FALSE; /* Get the text to put on the pasteboard. */ long_u llen = 0; char_u *str = 0; int motion_type = clip_convert_selection(&str, &llen, cbd); if (motion_type < 0) goto releasepool; /* TODO: Avoid overflow. */ int len = (int)llen; if (output_conv.vc_type != CONV_NONE) { char_u *conv_str = string_convert(&output_conv, str, &len); if (conv_str) { vim_free(str); str = conv_str; } } if (len > 0) { NSString *string = [[NSString alloc] initWithBytes:str length:len encoding:NSUTF8StringEncoding]; /* See clip_mch_request_selection() for info on pasteboard types. */ NSPasteboard *pb = [NSPasteboard generalPasteboard]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType, NSPasteboardTypeString, nil]; #else NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType, NSStringPboardType, nil]; #endif [pb declareTypes:supportedTypes owner:nil]; NSNumber *motion = [NSNumber numberWithInt:motion_type]; NSArray *plist = [NSArray arrayWithObjects:motion, string, nil]; [pb setPropertyList:plist forType:VimPboardType]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 [pb setString:string forType:NSPasteboardTypeString]; #else [pb setString:string forType:NSStringPboardType]; #endif [string release]; } vim_free(str); releasepool: [pool release]; } #endif /* FEAT_CLIPBOARD */ #ifdef FEAT_RELTIME /* * The following timer code is based on a Gist by Jorgen Lundman: * * https://gist.github.com/lundman */ typedef struct macos_timer macos_timer_T; static void _timer_cancel(void *arg UNUSED) { // This is not currently used, but it might be useful in the future and // it is non-trivial enough to provide as usable implementation. # if 0 macos_timer_T *timerid = (macos_timer_T *)arg; dispatch_release(timerid->tim_timer); dispatch_release(timerid->tim_queue); timerid->tim_timer = NULL; timerid->tim_queue = NULL; free(timerid); # endif } static void _timer_handler(void *arg) { macos_timer_T *timerid = (macos_timer_T *)arg; union sigval sv; sv.sival_ptr = timerid->tim_arg; if (timerid->tim_func != NULL) timerid->tim_func(sv); } static uint64_t itime_to_ns(const struct timespec *it) { time_t sec = it->tv_sec; long nsec = it->tv_nsec; uint64_t ns = NSEC_PER_SEC * sec + nsec; return ns == 0 ? DISPATCH_TIME_FOREVER : ns; } /* * A partial emulation of the POSIX timer_create function. * * The limitations and differences include: * * - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid * values. * - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time * source is used internally. * - The only notification method supported is SIGEV_THREAD. */ inline int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid) { macos_timer_T *timer = NULL; // We only support real time and monotonic clocks; and SIGEV_THREAD // notification. In practice, there is no difference between the two // types of clocks on MacOS - we always use the mach_machine_time // source. if ( (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC) || sevp->sigev_notify != SIGEV_THREAD) { semsg("clockid: %d %d", clockid, CLOCK_REALTIME); semsg("notify: %d %d", sevp->sigev_notify, SIGEV_THREAD); errno = ENOTSUP; return -1; } timer = (macos_timer_T *)malloc(sizeof(macos_timer_T)); if (timer == NULL) { errno = ENOMEM; return -1; } *timerid = timer; timer->tim_queue = dispatch_queue_create( "org.vim.timerqueue", NULL); if (timer->tim_queue == NULL) { errno = ENOMEM; return -1; } timer->tim_timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue); if (timer->tim_timer == NULL) { errno = ENOMEM; return -1; } timer->tim_func = sevp->sigev_notify_function; timer->tim_arg = sevp->sigev_value.sival_ptr; dispatch_set_context(timer->tim_timer, timer); dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler); dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel); dispatch_resume(timer->tim_timer); return 0; } /* * A partial emulation of the POSIX timer_settime function. * * The limitations and differences include: * * - The flags argument is ignored. The supplied new_value is therefore * always treated as a relative time. * - The old_value argument is ignored. */ int timer_settime( timer_t timerid, int unused_flags UNUSED, const struct itimerspec *new_value, struct itimerspec *old_value UNUSED) { uint64_t first_shot = itime_to_ns(&new_value->it_value); if (timerid == NULL) return 0; if (first_shot == DISPATCH_TIME_FOREVER) { dispatch_source_set_timer( timerid->tim_timer, first_shot, first_shot, 0); } else { uint64_t interval = itime_to_ns(&new_value->it_interval); dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot); dispatch_source_set_timer(timerid->tim_timer, start, interval, 0); } return 0; } /* * An emulation of the POSIX timer_delete function. * * Disabled because it is not currently used, but an implemented provided * for completeness and possible future use. */ int timer_delete(timer_t timerid) { /* Calls _timer_cancel() */ if (timerid != NULL) dispatch_source_cancel(timerid->tim_timer); return 0; } #endif /* FEAT_RELTIME */ #ifdef FEAT_SOUND static NSMutableDictionary<NSNumber*, NSSound*> *sounds_list = nil; /// A delegate for handling when a sound has stopped playing, in /// order to clean up the sound and to send a callback. @interface SoundDelegate : NSObject<NSSoundDelegate>; - (id) init:(long) sound_id callback:(soundcb_T*) callback; - (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag; @property (readonly) long sound_id; @property (readonly) soundcb_T *callback; @end @implementation SoundDelegate - (id) init:(long) sound_id callback:(soundcb_T*) callback { if ([super init]) { _sound_id = sound_id; _callback = callback; } return self; } - (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag { if (sounds_list != nil) { if (_callback) { call_sound_callback(_callback, _sound_id, flag ? 0 : 1); delete_sound_callback(_callback); _callback = NULL; } [sounds_list removeObjectForKey:[NSNumber numberWithLong:_sound_id]]; } // Release itself. Do that here instead of earlier because NSSound only // holds weak reference to this object. [self release]; } @end void process_cfrunloop(void) { if (sounds_list != nil && [sounds_list count] > 0) { // Continually drain the run loop of events. Currently, this // is only used for processing sound callbacks, because // NSSound relies of this runloop to call back to the // delegate. @autoreleasepool { while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; // do nothing } } } bool sound_mch_play(const char_u* sound_name, long sound_id, soundcb_T *callback, bool playfile) { @autoreleasepool { NSString *sound_name_ns = [[[NSString alloc] initWithUTF8String:(const char*)sound_name] autorelease]; NSSound* sound = playfile ? [[[NSSound alloc] initWithContentsOfFile:sound_name_ns byReference:YES] autorelease] : [NSSound soundNamed:sound_name_ns]; if (!sound) { return false; } if (sounds_list == nil) { sounds_list = [[NSMutableDictionary<NSNumber*, NSSound*> alloc] init]; } sounds_list[[NSNumber numberWithLong:sound_id]] = sound; // Make a delegate to handle when the sound stops. No need to call // autorelease because NSSound only holds a weak reference to it. SoundDelegate *delegate = [[SoundDelegate alloc] init:sound_id callback:callback]; [sound setDelegate:delegate]; [sound play]; } return true; } void sound_mch_stop(long sound_id) { @autoreleasepool { NSSound *sound = sounds_list[[NSNumber numberWithLong:sound_id]]; if (sound != nil) { // Stop the sound. No need to release it because the delegate will do // it for us. [sound stop]; } } } void sound_mch_clear(void) { if (sounds_list != nil) { @autoreleasepool { for (NSSound *sound in [sounds_list allValues]) { [sound stop]; } [sounds_list release]; sounds_list = nil; } } } void sound_mch_free(void) { sound_mch_clear(); } #endif // FEAT_SOUND /* Lift the compiler warning suppression. */ #if defined(__clang__) && defined(__STRICT_ANSI__) # pragma clang diagnostic pop # pragma clang diagnostic pop #endif