diff --git a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts index 8e189b87af..c67ccc09d0 100644 --- a/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts +++ b/packages/cursorless-vscode/src/ide/vscode/hats/VscodeHatRenderer.ts @@ -7,7 +7,7 @@ import { } from "@cursorless/common"; import { VscodeApi } from "@cursorless/vscode-common"; import { cloneDeep, isEqual } from "lodash"; -import * as fs from "node:fs"; +import * as fs from "fs/promises"; import * as path from "node:path"; import * as vscode from "vscode"; import { vscodeGetConfigurationString } from "../VscodeConfiguration"; @@ -24,6 +24,7 @@ import { defaultShapeAdjustments, } from "./shapeAdjustments"; import { performPr1868ShapeUpdateInit } from "./performPr1868ShapeUpdateInit"; +import { TextDecoder } from "util"; const CURSORLESS_HAT_SHAPES_SUFFIX = ".svg"; @@ -65,7 +66,8 @@ export default class VscodeHatRenderer { private notifier: Notifier<[]> = new Notifier(); private lastSeenEnabledHatStyles: ExtendedHatStyleMap = {}; private hatsDirWatcherDisposable?: vscode.Disposable; - private hatShapeOverrides: Record = {}; + private hatShapeOverrides: Record = {}; + private decoder: TextDecoder = new TextDecoder("utf-8"); constructor( private vscodeApi: VscodeApi, @@ -133,10 +135,13 @@ export default class VscodeHatRenderer { if (hatsDir) { await this.updateShapeOverrides(hatsDir); - if (fs.existsSync(hatsDir)) { + try { + await fs.access(hatsDir); this.hatsDirWatcherDisposable = watchDir(hatsDir, () => this.updateShapeOverrides(hatsDir), ); + } catch (e) { + console.error("cannot watch hatsDir", hatsDir, e); } } else { this.hatShapeOverrides = {}; @@ -150,7 +155,10 @@ export default class VscodeHatRenderer { for (const file of files) { const name = path.basename(file, CURSORLESS_HAT_SHAPES_SUFFIX); - this.hatShapeOverrides[name] = file; + this.hatShapeOverrides[name] = vscode.Uri.from({ + scheme: "file", + path: file, + }); } await this.recomputeDecorations(); @@ -207,35 +215,41 @@ export default class VscodeHatRenderer { ); const hatSvgMap = Object.fromEntries( - HAT_SHAPES.map((shape) => { - const { sizeAdjustment = 0, verticalOffset = 0 } = - defaultShapeAdjustments[shape]; - - const { - sizeAdjustment: userIndividualSizeAdjustment = 0, - verticalOffset: userIndividualVerticalOffset = 0, - } = userIndividualAdjustments[shape] ?? {}; - - const scaleFactor = - 1 + - (sizeAdjustment + userSizeAdjustment + userIndividualSizeAdjustment) / + await Promise.all( + HAT_SHAPES.map(async (shape) => { + const { sizeAdjustment = 0, verticalOffset = 0 } = + defaultShapeAdjustments[shape]; + + const { + sizeAdjustment: userIndividualSizeAdjustment = 0, + verticalOffset: userIndividualVerticalOffset = 0, + } = userIndividualAdjustments[shape] ?? {}; + + const scaleFactor = + 1 + + (sizeAdjustment + + userSizeAdjustment + + userIndividualSizeAdjustment) / + 100; + + const finalVerticalOffsetEm = + (verticalOffset + + userVerticalOffset + + userIndividualVerticalOffset) / 100; - const finalVerticalOffsetEm = - (verticalOffset + userVerticalOffset + userIndividualVerticalOffset) / - 100; - - return [ - shape, - this.processSvg( - this.fontMeasurements, + return [ shape, - scaleFactor, - defaultShapeAdjustments[shape].strokeFactor ?? 1, - finalVerticalOffsetEm, - ), - ]; - }), + await this.processSvg( + this.fontMeasurements, + shape, + scaleFactor, + defaultShapeAdjustments[shape].strokeFactor ?? 1, + finalVerticalOffsetEm, + ), + ]; + }), + ), ); this.decorationMap = Object.fromEntries( @@ -373,22 +387,24 @@ export default class VscodeHatRenderer { * @param hatVerticalOffsetEm How far off top of characters should hats be * @returns An object with the new SVG and its measurements */ - private processSvg( + private async processSvg( fontMeasurements: FontMeasurements, shape: HatShape, scaleFactor: number, strokeFactor: number, hatVerticalOffsetEm: number, - ): SvgInfo | null { + ): Promise { const iconPath = this.hatShapeOverrides[shape] ?? - path.join( - this.extensionContext.extensionPath, + vscode.Uri.joinPath( + this.extensionContext.extensionUri, "images", "hats", `${shape}.svg`, ); - const rawSvg = fs.readFileSync(iconPath, "utf8"); + const rawSvg = this.decoder.decode( + await vscode.workspace.fs.readFile(iconPath), + ); const { characterWidth, characterHeight, fontSize } = fontMeasurements; if (!this.checkSvg(shape, rawSvg)) {