Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support native macOS monospace font (SF Mono) #1463

Merged
merged 1 commit into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 15 additions & 4 deletions runtime/doc/gui.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1129,11 +1129,22 @@ That's all. XLFDs are not used. For Chinese this is reported to work well: >
<
(Replace gui_gtk2 with gui_gtk3 for the GTK+ 3 GUI)

For Mac OSX you can use something like this: >
:set guifont=Monaco:h10
Also see 'macatsui', it can help fix display problems {not in MacVim}.
In MacVim, fonts with spaces are set like this: >
MacVim *macvim-guifont*

For MacVim you can use something like this: >
:set guifont=Menlo:h10
Fonts with spaces are set like this: >
:set guifont=DejaVu\ Sans\ Mono:h13
To use bold/italic fonts, use the fully specified PostScript name of the
font, like so: >
:set guifont=Menlo-Bold:h13
To use the system native monospace font (which is SF Mono in new macOS
versions), use the `-monospace-` keyword: >
:set guifont=-monospace-:h12
You can also specify the font weight of the native monospace font (refer to
Apple documentation for `NSFontWeight` for possible values): >
:set guifont=-monospace-Light:h11
:set guifont=-monospace-Medium:h11
<
Mono-spaced fonts *E236*

Expand Down
3 changes: 3 additions & 0 deletions runtime/doc/gui_mac.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ These are the non-standard options that MacVim supports:
'fuoptions' 'macligatures' 'macmeta' 'macthinstrokes'
'toolbariconsize' 'transparency'

These are GUI-related Vim options that have MacVim-specific behaviors:
'guifont'

*macvim-commands*
These are the non-standard commands that MacVim supports:
|:macaction| |:macmenu|
Expand Down
1 change: 1 addition & 0 deletions runtime/doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -8622,6 +8622,7 @@ macvim-encoding gui_mac.txt /*macvim-encoding*
macvim-find gui_mac.txt /*macvim-find*
macvim-full-screen gui_mac.txt /*macvim-full-screen*
macvim-gestures gui_mac.txt /*macvim-gestures*
macvim-guifont gui.txt /*macvim-guifont*
macvim-help-menu gui_mac.txt /*macvim-help-menu*
macvim-hints gui_mac.txt /*macvim-hints*
macvim-internal-variables gui_mac.txt /*macvim-internal-variables*
Expand Down
1 change: 1 addition & 0 deletions src/MacVim/MMCoreTextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ NS_ASSUME_NONNULL_BEGIN
// NSFontChanging methods
//
- (void)changeFont:(nullable id)sender;
- (NSFontPanelModeMask)validModesForFontPanel:(NSFontPanel *)fontPanel;

//
// NSMenuItemValidation
Expand Down
21 changes: 18 additions & 3 deletions src/MacVim/MMCoreTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -1299,9 +1299,10 @@ - (NSSize)minSize
MMMinRows * cellSize.height + insetSize.height + bot);
}

// Called when font panel selection has been made. Send the selected font to
// MMBackend so it would set guifont which will send a message back to MacVim to
// call MMWindowController::setFont.
// Called when font panel selection has been made or when adjusting font size
// using modifyFont/NSSizeUpFontAction. Send the selected font to MMBackend so
// it would set guifont which will send a message back to MacVim to call
// MMWindowController::setFont.
- (void)changeFont:(id)sender
{
NSFont *newFont = [sender convertFont:font];
Expand All @@ -1319,11 +1320,25 @@ - (void)changeFont:(id)sender
[data appendBytes:&len length:sizeof(unsigned)];
[data appendBytes:[name UTF8String] length:len];

// We don't update guifontwide for now, as panel font selection
// shouldn't affect them. This does mean Cmd +/- does not work for
// them for now.
const unsigned wideLen = 0;
[data appendBytes:&wideLen length:sizeof(unsigned)];

[[self vimController] sendMessage:SetFontMsgID data:data];
}
}
}

- (NSFontPanelModeMask)validModesForFontPanel:(NSFontPanel *)fontPanel
{
// Lets the user pick only the font face / size, as other properties as not
// useful. Still enable text/document colors as these affect the preview.
// Otherwise it could just be white text on white background in the preview.
return NSFontPanelModesMaskStandardModes & (~NSFontPanelModeMaskAllEffects | NSFontPanelModeMaskTextColorEffect | NSFontPanelModeMaskDocumentColorEffect);
}

/// Specifies whether the menu item should be enabled/disabled.
- (BOOL)validateMenuItem:(NSMenuItem *)item
{
Expand Down
39 changes: 38 additions & 1 deletion src/MacVim/MMVimController.m
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,44 @@ - (void)handleMessage:(int)msgid data:(NSData *)data
NSString *name = [[NSString alloc]
initWithBytes:(void*)bytes length:len
encoding:NSUTF8StringEncoding];
NSFont *font = [NSFont fontWithName:name size:size];
NSFont *font = nil;
if ([name hasPrefix:MMSystemFontAlias]) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
if (@available(macos 10.15, *)) {
NSFontWeight fontWeight = NSFontWeightRegular;
if (name.length > MMSystemFontAlias.length) {
const NSRange cmpRange = NSMakeRange(MMSystemFontAlias.length, name.length - MMSystemFontAlias.length);
if ([name compare:@"UltraLight" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightUltraLight;
else if ([name compare:@"Thin" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightThin;
else if ([name compare:@"Light" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightLight;
else if ([name compare:@"Regular" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightRegular;
else if ([name compare:@"Medium" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightMedium;
else if ([name compare:@"Semibold" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightSemibold;
else if ([name compare:@"Bold" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightBold;
else if ([name compare:@"Heavy" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightHeavy;
else if ([name compare:@"Black" options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame)
fontWeight = NSFontWeightBlack;
}
font = [NSFont monospacedSystemFontOfSize:size weight:fontWeight];
}
else
#endif
{
// Fallback to Menlo on older macOS versions that don't support the system monospace font API
font = [NSFont fontWithName:@"Menlo-Regular" size:size];
}
}
else {
font = [NSFont fontWithName:name size:size];
}
if (!font) {
// This should only happen if the system default font has changed
// name since MacVim was compiled in which case we fall back on
Expand Down
3 changes: 2 additions & 1 deletion src/MacVim/MacVim.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ enum {

extern NSString *VimFindPboardType;


// Alias for system monospace font name
extern NSString *MMSystemFontAlias;


@interface NSString (MMExtras)
Expand Down
2 changes: 2 additions & 0 deletions src/MacVim/MacVim.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
// Vim find pasteboard type (string contains Vim regex patterns)
NSString *VimFindPboardType = @"VimFindPboardType";

NSString *MMSystemFontAlias = @"-monospace-";

int ASLogLevel = MM_ASL_LEVEL_DEFAULT;


Expand Down
33 changes: 33 additions & 0 deletions src/MacVim/MacVimTests/MacVimTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#import <objc/runtime.h>

#import <Cocoa/Cocoa.h>

#import "Miscellaneous.h"
#import "MMAppController.h"
#import "MMApplication.h"
Expand Down Expand Up @@ -425,4 +427,35 @@ - (void) testCmdlineRowCalculation {
[self waitForVimClose];
}

/// Test that using "-monospace-" for system default monospace font works.
- (void) testGuifontSystemMonospace {
MMAppController *app = MMAppController.sharedInstance;

[app openNewWindow:NewWindowClean activate:YES];
[self waitForVimOpenAndMessages];

MMTextView *textView = [[[[app keyVimController] windowController] vimView] textView];
XCTAssertEqualObjects(@"Menlo-Regular", [[textView font] fontName]);

[self sendStringToVim:@":set guifont=-monospace-\n" withMods:0];
[self waitForEventHandlingAndVimProcess];
XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:11 weight:NSFontWeightRegular]);

[self sendStringToVim:@":set guifont=-monospace-Heavy:h12\n" withMods:0];
[self waitForEventHandlingAndVimProcess];
XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightHeavy]);

[[[app keyVimController] windowController] fontSizeUp:nil];
[self waitForEventHandlingAndVimProcess];
XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:13 weight:NSFontWeightHeavy]);

[[[app keyVimController] windowController] fontSizeDown:nil];
[self waitForEventHandlingAndVimProcess];
XCTAssertEqualObjects([textView font], [NSFont monospacedSystemFontOfSize:12 weight:NSFontWeightHeavy]);

// Clean up
[[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil];
[self waitForVimClose];
}

@end
3 changes: 2 additions & 1 deletion src/MacVim/Miscellaneous.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ enum {


@interface NSNumber (MMExtras)
// HACK to allow font size to be changed via menu (bound to Cmd+/Cmd-)
// Used by modifyFont:/convertFont: to allow font size to be changed via menu
// (bound to Cmd+/Cmd-) or using macaction fontSizeUp:/fontSizeDown:.
- (NSInteger)tag;
@end

Expand Down
46 changes: 44 additions & 2 deletions src/MacVim/gui_macvim.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@

static NSString *MMDefaultFontName = @"Menlo-Regular";
static int MMDefaultFontSize = 11;
static char *MMDefaultFontStr = "Menlo-Regular:h11";
static char *MMDefaultFontSizeStr = "h11";
static int MMMinFontSize = 6;
static int MMMaxFontSize = 100;

// This is duplicated in MMVimController. Could consolidate in the future.
static NSString *(system_font_weights[]) = { @"UltraLight", @"Thin", @"Light", @"Regular", @"Medium", @"Semibold", @"Bold", @"Heavy", @"Black" };

static BOOL MMShareFindPboard = YES;

static GuiFont gui_macvim_font_with_name(char_u *name);
Expand Down Expand Up @@ -1141,13 +1143,30 @@
componentsJoinedByString:@" "];
}

const BOOL isSystemFont = [fontName hasPrefix:MMSystemFontAlias];
if (isSystemFont) {
if (fontName.length > MMSystemFontAlias.length) {
BOOL invalidWeight = YES;
const NSRange cmpRange = NSMakeRange(MMSystemFontAlias.length, fontName.length - MMSystemFontAlias.length);
for (size_t i = 0; i < ARRAY_LENGTH(system_font_weights); i++) {
if ([fontName compare:system_font_weights[i] options:NSCaseInsensitiveSearch range:cmpRange] == NSOrderedSame) {
invalidWeight = NO;
break;
}
}
if (invalidWeight)
return NOFONT;
}
}

if (!parseFailed && [fontName length] > 0) {
if (size < MMMinFontSize) size = MMMinFontSize;
if (size > MMMaxFontSize) size = MMMaxFontSize;

// If the default font is requested we don't need to check if NSFont
// can load it. Otherwise we ask NSFont if it can load it.
if ([fontName isEqualToString:MMDefaultFontName]
|| isSystemFont
|| [NSFont fontWithName:fontName size:size])
return [[NSString alloc] initWithFormat:@"%@:h%d", fontName, size];
}
Expand All @@ -1170,7 +1189,9 @@
{
// If guifont is empty, and we want to fill in the orig value, suggest
// the default so the user can modify it.
if (add_match((char_u *)MMDefaultFontStr) != OK)
NSString *defaultFontStr = [NSString stringWithFormat:@"%@:h%d",
MMDefaultFontName, MMDefaultFontSize];
if (add_match((char_u *)[defaultFontStr UTF8String]) != OK)
return;
}

Expand All @@ -1185,6 +1206,27 @@
return;
}

if (!wide) {
// Add system-native monospace font alias to completion.
char buf[40];
[MMSystemFontAlias getCString:buf maxLength:ARRAY_LENGTH(buf) encoding:NSASCIIStringEncoding];
if (add_match((char_u*)buf) != OK)
return;
const size_t fontAliasLen = STRLEN(buf);
if (STRNCMP(xp->xp_pattern, buf, fontAliasLen) == 0) {
// We additionally complete with font weights like "bold". We only
// do so if starting with "-monospace-" already to avoid spamming
// the user with too many variations on this.
for (size_t i = 0; i < ARRAY_LENGTH(system_font_weights); i++) {
[system_font_weights[i] getCString:buf+fontAliasLen
maxLength:ARRAY_LENGTH(buf)-fontAliasLen
encoding:NSASCIIStringEncoding];
if (add_match((char_u*)buf) != OK)
return;
}
}
}

NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSArray<NSString *> *availableFonts;
if (wide)
Expand Down
29 changes: 29 additions & 0 deletions src/testdir/test_gui.vim
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,28 @@ func Test_set_guifont()
let &guifont = guifont_saved
endfunc

func Test_set_guifont_macvim()
CheckFeature gui_macvim
let guifont_saved = &guifont
let guifontwide_saved = &guifontwide

set guifont=-monospace-
call assert_equal('-monospace-:h11', getfontname())
set guifont=-monospace-Semibold
call assert_equal('-monospace-Semibold:h11', getfontname())

call assert_fails('set guifont=-monospace-SemiboldInvalidWeight', 'E596')

set guifont=Menlo\ Regular
call assert_equal('Menlo Regular:h11', getfontname())

set guifont=
call assert_equal('Menlo-Regular:h11', getfontname())

let &guifontwide = guifontwide_saved
let &guifont = guifont_saved
endfunc

func Test_set_guifontset()
CheckFeature xfontset
let skipped = ''
Expand Down Expand Up @@ -641,6 +663,13 @@ func Test_expand_guifont()
call assert_equal(['Menlo-Regular'], getcompletion('set guifont=Menl*lar$', 'cmdline'))
call assert_equal(['Menlo-Regular'], getcompletion('set guifontwide=Menl*lar$', 'cmdline'))

" Test system monospace font option. It's always the first option after
" the existing font.
call assert_equal('-monospace-', getcompletion('set guifont=', 'cmdline')[1])
call assert_equal('-monospace-', getcompletion('set guifont=-monospace-', 'cmdline')[0])
call assert_equal('-monospace-UltraLight', getcompletion('set guifont=-monospace-', 'cmdline')[1])
call assert_equal(['-monospace-Medium'], getcompletion('set guifont=-monospace-Med', 'cmdline'))

" Make sure non-monospace fonts are filtered out only in 'guifont'
call assert_equal([], getcompletion('set guifont=Hel*tica$', 'cmdline'))
call assert_equal(['Helvetica'], getcompletion('set guifontwide=Hel*tica$', 'cmdline'))
Expand Down