Skip to content

Commit

Permalink
Fixes .ttc files are huge and after adding their support, load time…
Browse files Browse the repository at this point in the history
…s have increased #6
  • Loading branch information
talhabalaj committed Jan 29, 2024
1 parent 0b8a372 commit 16148a7
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 122 deletions.
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 100
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 0.0.9
- Adds Global Font Cache to improve performance
- Adds `Font Changer: Clear Global Font Cache` command to clear the cache

## 0.0.8
- Updated keybinds

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ The Font Changer Extension for Visual Studio Code is a handy tool that allows yo

## Release Notes

### 0.0.9
- Adds Global Font Cache to improve performance
- Adds `Font Changer: Clear Global Font Cache` command to clear the cache

### 0.0.8
- Updated keybinds

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "actual-font-changer",
"displayName": "Font Changer",
"description": "Quickly changes fonts by selecting from a installed fonts",
"version": "0.0.8",
"version": "0.0.9",
"publisher": "talhabalaj",
"repository": {
"url": "https://github.com/talhabalaj/vscode-font-changer"
Expand All @@ -21,6 +21,10 @@
{
"command": "font-changer.selectFont",
"title": "Font Changer: Select Global font"
},
{
"command": "font-changer.clearFontCache",
"title": "Font Changer: Clear Font Cache"
}
],
"keybindings": [
Expand Down
98 changes: 54 additions & 44 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,75 @@
import * as vscode from "vscode";
import "@total-typescript/ts-reset";
import { getFonts } from "./utils/fonts";
import { CachedFontsMap, getFonts } from "./utils/fonts";

const CACHE_KEY = "actual-font-change-fonts-cache";

export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
"font-changer.selectFont",
async () => {
const fonts = vscode.window.withProgress(
{
location: vscode.ProgressLocation.Window,
cancellable: false,
title: "Loading fonts...",
},
async (progress) => {
progress.report({ increment: 0 });
let selectFontCommand = vscode.commands.registerCommand("font-changer.selectFont", async () => {
const fonts = vscode.window.withProgress(
{
location: vscode.ProgressLocation.Window,
cancellable: false,
title: "Loading fonts...",
},
async (progress) => {
progress.report({ increment: 0 });

const fonts = await getFonts();
const cache = context.globalState.get(CACHE_KEY, {} as CachedFontsMap);
console.log(cache);
const fonts = await getFonts(process.platform, cache);

progress.report({ increment: 100 });
await context.globalState.update(CACHE_KEY, cache);
progress.report({ increment: 100 });

return fonts;
}
);
return fonts;
}
);

const config = vscode.workspace.getConfiguration("editor");
const oldFont = config.get("fontFamily");

const config = vscode.workspace.getConfiguration("editor");
const oldFont = config.get("fontFamily");
let currentPreviewFont: string | undefined = undefined;
let timeout: NodeJS.Timeout | undefined = undefined;

let currentPreviewFont: string | undefined = undefined;
let timeout: NodeJS.Timeout | undefined = undefined;
const previewFont = async () => {
await config.update("fontFamily", currentPreviewFont, true);
};

const previewFont = async () => {
await config.update("fontFamily", currentPreviewFont, true);
};
const selectedFont = await vscode.window.showQuickPick(fonts, {
onDidSelectItem(item) {
if (timeout) {
clearTimeout(timeout);
}

const selectedFont = await vscode.window.showQuickPick(fonts, {
onDidSelectItem(item) {
if (timeout) {
clearTimeout(timeout);
}
if (typeof item === "string") {
currentPreviewFont = item;
}

if (typeof item === "string") {
currentPreviewFont = item;
}
timeout = setTimeout(previewFont, 100);
},
placeHolder: "Select a font",
title: "Change editor font",
});

timeout = setTimeout(previewFont, 100);
},
placeHolder: "Select a font",
title: "Change editor font",
});
clearTimeout(timeout);

clearTimeout(timeout);
if (selectedFont) {
await config.update("fontFamily", selectedFont, true);
} else {
await config.update("fontFamily", oldFont, true);
}
});

if (selectedFont) {
await config.update("fontFamily", selectedFont, true);
} else {
await config.update("fontFamily", oldFont, true);
}
let clearFontCacheCommand = vscode.commands.registerCommand(
"font-changer.clearFontCache",
async () => {
context.globalState.update(CACHE_KEY, {} as CachedFontsMap);
}
);

context.subscriptions.push(disposable);
context.subscriptions.push(selectFontCommand);
context.subscriptions.push(clearFontCacheCommand);
}

// This method is called when your extension is deactivated
Expand Down
145 changes: 70 additions & 75 deletions src/utils/fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,62 @@ import * as vscode from "vscode";
import { Font, open as openFont } from "fontkit";
import { getFilesOfDir } from "./fs";

type DateString = string;
export interface CachedFontsMap {
[key: `${string}-${DateString}`]: string[];
}

export async function getFonts(
os: (typeof process)["platform"] = process.platform
os: (typeof process)["platform"] = process.platform,
cachedFonts: CachedFontsMap
) {
const loadedFonts = new Set<string>();
const installedFontPaths = getInstalledFontsPaths(os);
const detectedFontsFiles = await Promise.all(
installedFontPaths.map(findFonts)
).then((f) => f.flat());
const fonts = await Promise.all(detectedFontsFiles.map(loadFont)).then((f) =>
const detectedFontsFiles = await Promise.all(installedFontPaths.map(findFonts)).then((f) =>
f.flat()
);
return fonts
.filter((font) => isMonoFont(font))
.map((f) =>
loadedFonts.has(f.familyName)
? undefined
: loadedFonts.add(f.familyName) && f.familyName
)
.filter(Boolean);
const monoFontsFamilies = await Promise.all(
detectedFontsFiles.map(async (file) => {
const cacheKey = `${file.uri.fsPath}-${file.stat.mtime}` as const;
if (!(cacheKey in cachedFonts)) {
const font = await loadFont(file.uri);
cachedFonts[cacheKey] = font.filter(isMonoFont).map((f) => f.familyName);
} else {
console.log("Cache Hit");
}

return cachedFonts[cacheKey];
})
).then((f) => f.flat());

const loadedFontFamilies = new Set<string>();
const uniqueFonts = monoFontsFamilies.filter((name) => {
if (!loadedFontFamilies.has(name)) {
loadedFontFamilies.add(name);
return true;
}
return false;
});

return uniqueFonts;
}

function isFontFile(filePath: string) {
return (
filePath.endsWith(".otf") ||
filePath.endsWith(".ttf") ||
filePath.endsWith(".ttc")
);
return filePath.endsWith(".otf") || filePath.endsWith(".ttf") || filePath.endsWith(".ttc");
}

function isMonoFont(font: Font) {
try {
const iGlyph = font.glyphsForString("i")[0];
const mGlyph = font.glyphsForString("m")[0];

if (
return (
iGlyph &&
mGlyph &&
iGlyph.advanceWidth === mGlyph.advanceWidth &&
font.characterSet.includes(65) // 65 is captial A
) {
return true;
}
);
} catch (e) {
console.log("Error determining mono font", e, font);
console.error("Error determining mono font", e, font);
}

return false;
Expand All @@ -56,86 +68,69 @@ function isMonoFont(font: Font) {
* @returns Fonts in a font file
*/
async function loadFont(path: vscode.Uri): Promise<Font[]> {
let font: Font | undefined = undefined;

try {
font = await openFont(path.fsPath);
const font = await openFont(path.fsPath);
// @ts-ignore Typing for font kit isn't working correctly
if (font.type === "TTC") {
// @ts-ignore Typing for font kit isn't working correctly
return font.fonts;
} else {
return [font];
}
} catch (e) {
console.error("Error loading font: " + path, e);
}

if (!font) {
console.error("Error loading font:", path.fsPath, e);
return [];
}

// @ts-ignore Typing for font kit aren't working correctly
if (font.type === "TTC") {
// @ts-ignore
const fonts = font.fonts;
return fonts;
}

return [font];
}

async function findFonts(rootUri: vscode.Uri): Promise<vscode.Uri[]> {
let fileStat: vscode.FileStat | null = null;

async function findFonts(rootUri: vscode.Uri): Promise<
{
uri: vscode.Uri;
stat: vscode.FileStat;
}[]
> {
try {
fileStat = await vscode.workspace.fs.stat(rootUri);
} catch (e) {
if (e instanceof vscode.FileSystemError) {
console.error("Did not find font file: " + rootUri.path);
} else {
console.error("error: " + rootUri.path, e);
}
}

if (!fileStat) {
return [];
}

if (fileStat.type === vscode.FileType.Directory) {
const directories = await getFilesOfDir(rootUri);
const results = await Promise.all(directories.map(findFonts));
return results.flat();
} else if (fileStat.type === vscode.FileType.File) {
if (isFontFile(rootUri.path)) {
return [rootUri];
const fileStat = await vscode.workspace.fs.stat(rootUri);
if (fileStat.type === vscode.FileType.Directory) {
const directories = await getFilesOfDir(rootUri);
const results = await Promise.all(directories.map(findFonts));
return results.flat();
} else if (fileStat.type === vscode.FileType.File && isFontFile(rootUri.path)) {
return [
{
uri: rootUri,
stat: fileStat,
},
];
}
} catch (e) {
console.error("Error finding fonts:", rootUri.path, e);
}

return [];
}

export function getInstalledFontsPaths(os: (typeof process)["platform"]) {
const uris: vscode.Uri[] = [];
const userDir = vscode.Uri.file(
process.env.HOME || process.env.USERPROFILE || "/root"
);
const userDir = vscode.Uri.file(process.env.HOME || process.env.USERPROFILE || "/root");

if (os === "win32") {
const windowsUris = [
return [
vscode.Uri.file("c:\\Windows\\Fonts"),
vscode.Uri.joinPath(userDir, `AppData\\Local\\Microsoft\\Windows\\Fonts`),
];
uris.push(...windowsUris);
} else if (os === "linux") {
const linuxUris = [
return [
vscode.Uri.parse("/usr/share/fonts/"),
vscode.Uri.parse("/usr/local/share/fonts/"),
vscode.Uri.joinPath(userDir, `.fonts`),
vscode.Uri.joinPath(userDir, `.local/share/fonts`),
];
uris.push(...linuxUris);
} else if (os === "darwin") {
const darwinUris = [
return [
vscode.Uri.joinPath(userDir, "Library/Fonts"),
vscode.Uri.parse("/Library/Fonts/"),
vscode.Uri.parse("/System/Library/Fonts/"),
];
uris.push(...darwinUris);
}

return uris;
return [];
}

0 comments on commit 16148a7

Please sign in to comment.