diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-h-size-syncs-both-family-and-size-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-h-size-syncs-both-family-and-size-1-snap.png new file mode 100644 index 0000000..0d2e9f9 Binary files /dev/null and b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-h-size-syncs-both-family-and-size-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-syncs-to-vv-fontfamily-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-syncs-to-vv-fontfamily-1-snap.png new file mode 100644 index 0000000..c254e0d Binary files /dev/null and b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-set-guifont-family-syncs-to-vv-fontfamily-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-renders-with-the-requested-font-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-renders-with-the-requested-font-1-snap.png new file mode 100644 index 0000000..409b8b2 Binary files /dev/null and b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-renders-with-the-requested-font-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-updates-guifont-reverse-sync-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-updates-guifont-reverse-sync-1-snap.png new file mode 100644 index 0000000..4d7b962 Binary files /dev/null and b/packages/browser-renderer/src/__tests__/__image_snapshots__/font-sync-test-ts-font-sync-between-v-vset-fontfamily-and-guifont-v-vset-fontfamily-updates-guifont-reverse-sync-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-match-snapshot-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-match-snapshot-1-snap.png index 427643b..9343ac8 100644 Binary files a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-match-snapshot-1-snap.png and b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-match-snapshot-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-1-snap.png index 17212fc..efcf075 100644 Binary files a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-1-snap.png and b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-2-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-2-snap.png index 32216f4..a7f649c 100644 Binary files a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-2-snap.png and b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-overlap-chars-2-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-show-undercurl-behind-the-text-1-snap.png b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-show-undercurl-behind-the-text-1-snap.png index fcbb2f0..52370e3 100644 Binary files a/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-show-undercurl-behind-the-text-1-snap.png and b/packages/browser-renderer/src/__tests__/__image_snapshots__/screen-test-ts-screen-show-undercurl-behind-the-text-1-snap.png differ diff --git a/packages/browser-renderer/src/__tests__/fontSync.test.ts b/packages/browser-renderer/src/__tests__/fontSync.test.ts new file mode 100644 index 0000000..c42a247 --- /dev/null +++ b/packages/browser-renderer/src/__tests__/fontSync.test.ts @@ -0,0 +1,82 @@ +/** @jest-environment node */ + +import puppeteer from 'puppeteer'; +import { PORT } from 'config/jest/testServer'; + +import type { Browser, Page } from 'puppeteer'; + +const setup = async (browser: Browser): Promise => { + const page = await browser.newPage(); + await page.setViewport({ width: 300, height: 200, deviceScaleFactor: 2 }); + await page.goto(`http://localhost:${PORT}`); + await page.waitForSelector('input'); + return page; +}; + +const run = async (page: Page, cmd: string) => { + await page.keyboard.type(`:${cmd}`); + await page.keyboard.press('Enter'); +}; + +describe('Font sync between VVset fontfamily and &guifont', () => { + jest.setTimeout(30000); + + let browser: Browser; + let page: Page; + + beforeAll(async () => { + browser = await puppeteer.launch({ + headless: true, + slowMo: 10, + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }); + }); + + afterAll(async () => { + // await browser.close(); + }); + + beforeEach(async () => { + page = await setup(browser); + }); + + afterEach(async () => { + await page.close(); + }); + + test('VVset fontfamily renders with the requested font', async () => { + await run(page, 'VVset fontfamily=Monaco'); + await run(page, 'VVset fontsize=14'); + await page.keyboard.type('iHello Monaco'); + await page.keyboard.press('Escape'); + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + test('set guifont= syncs to VV fontfamily', async () => { + await run(page, 'set guifont=Menlo'); + await run(page, 'VVset fontsize=14'); + await page.keyboard.type('iguifont Menlo'); + await page.keyboard.press('Escape'); + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + test('set guifont=:h syncs both family and size', async () => { + await run(page, 'set guifont=Courier\\ New:h16'); + await page.keyboard.type('iCourier h16'); + await page.keyboard.press('Escape'); + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + test('VVset fontfamily updates &guifont (reverse sync)', async () => { + await run(page, 'VVset fontfamily=Monaco'); + await run(page, 'VVset fontsize=15'); + // `:set guifont?` echoes the value to the command line, which the + // screenshot captures. + await run(page, 'set guifont?'); + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); +}); diff --git a/packages/browser-renderer/src/screen.ts b/packages/browser-renderer/src/screen.ts index 1146aa4..805b9a8 100644 --- a/packages/browser-renderer/src/screen.ts +++ b/packages/browser-renderer/src/screen.ts @@ -443,18 +443,6 @@ const screen = ({ cursorEl.style.transform = `translate(${left}px, ${top}px)`; }; - const optionSet = { - guifont: (newFont: string) => { - const [newFontFamily, newFontSize] = newFont.trim().split(':h'); - if (newFontFamily && newFontFamily !== '') { - nvim.command(`VVset fontfamily=${newFontFamily.replace(/_/g, '\\ ')}`); - if (newFontSize && newFontFamily !== '') { - nvim.command(`VVset fontsize=${newFontSize}`); - } - } - }, - }; - const recalculateHighlightTable = () => { (Object.keys(highlightTable) as unknown as number[]).forEach((id) => { if (id > 0) { @@ -573,16 +561,8 @@ const screen = ({ modeInfoSet = props[0][1].reduce((r, modeInfo) => ({ ...r, [modeInfo.name]: modeInfo }), {}); }, - option_set: (options) => { - options.forEach(([option, value]) => { - // @ts-expect-error TODO - if (optionSet[option]) { - // @ts-expect-error TODO - optionSet[option](value); - } else { - // console.warn('Unknown option', option, value); // eslint-disable-line no-console - } - }); + option_set: () => { + /* empty */ }, mode_change: (modes) => { diff --git a/packages/electron/bin/vvset.vim b/packages/electron/bin/vvset.vim index b3381c9..4a18694 100644 --- a/packages/electron/bin/vvset.vim +++ b/packages/electron/bin/vvset.vim @@ -32,6 +32,52 @@ let g:vv_default_settings = { let g:vv_settings = deepcopy(g:vv_default_settings) +" Guard against infinite recursion when syncing fontfamily/fontsize and &guifont. +let g:vv_syncing_font = 0 + +" Apply VV's current fontfamily + fontsize to nvim's &guifont. +" Builds ":h,,..." so :set guifont? matches VV. +function! s:SyncGuifont() + if g:vv_syncing_font | return | endif + let l:parts = split(get(g:vv_settings, 'fontfamily', ''), ',') + if empty(l:parts) | return | endif + let l:size = get(g:vv_settings, 'fontsize', '') + let l:first = escape(l:parts[0], ' \') + if l:size !=# '' && l:size != 0 + let l:first .= ':h' . l:size + endif + let l:rest = map(l:parts[1:], 'escape(v:val, " \\")') + let l:guifont = empty(l:rest) ? l:first : l:first . ',' . join(l:rest, ',') + let g:vv_syncing_font = 1 + try + execute 'set guifont=' . l:guifont + catch + endtry + let g:vv_syncing_font = 0 +endfunction + +" Parse &guifont and apply its first entry's family + :h to VV. +function! s:SyncFromGuifont(value) + if g:vv_syncing_font || a:value ==# '' | return | endif + let l:first = split(a:value, ',')[0] + let l:family = substitute(l:first, ':h.*$', '', '') + if l:family ==# '' | return | endif + let l:size = matchstr(l:first, ':h\zs[0-9.]\+') + let g:vv_syncing_font = 1 + call VVsetItem('fontfamily=' . l:family) + if l:size !=# '' + call VVsetItem('fontsize=' . l:size) + endif + let g:vv_syncing_font = 0 +endfunction + +augroup VVFontSync + autocmd! + autocmd OptionSet guifont call s:SyncFromGuifont(v:option_new) + " Catch &guifont set in init.vim — OptionSet does not fire during startup. + autocmd VimEnter * call s:SyncFromGuifont(&guifont) +augroup END + " Custom VVset command, mimic default set command (:help set) with " settings specified in g:vv_default_settings function! VVset(...) @@ -124,6 +170,9 @@ function! VVsetItem(name) if has_key(g:vv_settings, l:name) let g:vv_settings[l:name] = l:value call rpcnotify(get(g:, "vv_channel", 1), "vv:set", l:name, l:value) + if l:name ==# 'fontfamily' || l:name ==# 'fontsize' + call s:SyncGuifont() + endif else echoerr "Unknown option: ".l:name endif diff --git a/packages/electron/package.json b/packages/electron/package.json index 1df0c96..dfb6ff9 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -2,7 +2,7 @@ "name": "@vvim/electron", "description": "Neovim GUI Client", "author": "Igor Gladkoborodov ", - "version": "2.6.2", + "version": "2.6.3", "private": true, "keywords": [ "vim", diff --git a/packages/server/bin/vvset.vim b/packages/server/bin/vvset.vim index b3381c9..4a18694 100644 --- a/packages/server/bin/vvset.vim +++ b/packages/server/bin/vvset.vim @@ -32,6 +32,52 @@ let g:vv_default_settings = { let g:vv_settings = deepcopy(g:vv_default_settings) +" Guard against infinite recursion when syncing fontfamily/fontsize and &guifont. +let g:vv_syncing_font = 0 + +" Apply VV's current fontfamily + fontsize to nvim's &guifont. +" Builds ":h,,..." so :set guifont? matches VV. +function! s:SyncGuifont() + if g:vv_syncing_font | return | endif + let l:parts = split(get(g:vv_settings, 'fontfamily', ''), ',') + if empty(l:parts) | return | endif + let l:size = get(g:vv_settings, 'fontsize', '') + let l:first = escape(l:parts[0], ' \') + if l:size !=# '' && l:size != 0 + let l:first .= ':h' . l:size + endif + let l:rest = map(l:parts[1:], 'escape(v:val, " \\")') + let l:guifont = empty(l:rest) ? l:first : l:first . ',' . join(l:rest, ',') + let g:vv_syncing_font = 1 + try + execute 'set guifont=' . l:guifont + catch + endtry + let g:vv_syncing_font = 0 +endfunction + +" Parse &guifont and apply its first entry's family + :h to VV. +function! s:SyncFromGuifont(value) + if g:vv_syncing_font || a:value ==# '' | return | endif + let l:first = split(a:value, ',')[0] + let l:family = substitute(l:first, ':h.*$', '', '') + if l:family ==# '' | return | endif + let l:size = matchstr(l:first, ':h\zs[0-9.]\+') + let g:vv_syncing_font = 1 + call VVsetItem('fontfamily=' . l:family) + if l:size !=# '' + call VVsetItem('fontsize=' . l:size) + endif + let g:vv_syncing_font = 0 +endfunction + +augroup VVFontSync + autocmd! + autocmd OptionSet guifont call s:SyncFromGuifont(v:option_new) + " Catch &guifont set in init.vim — OptionSet does not fire during startup. + autocmd VimEnter * call s:SyncFromGuifont(&guifont) +augroup END + " Custom VVset command, mimic default set command (:help set) with " settings specified in g:vv_default_settings function! VVset(...) @@ -124,6 +170,9 @@ function! VVsetItem(name) if has_key(g:vv_settings, l:name) let g:vv_settings[l:name] = l:value call rpcnotify(get(g:, "vv_channel", 1), "vv:set", l:name, l:value) + if l:name ==# 'fontfamily' || l:name ==# 'fontsize' + call s:SyncGuifont() + endif else echoerr "Unknown option: ".l:name endif