refactor: big rewrite

This commit is contained in:
iCrawl 2021-02-10 04:24:42 +01:00
parent 3b3f41af23
commit aa5e0c97da
No known key found for this signature in database
GPG key ID: 1AB888B16355FBB2
21 changed files with 4232 additions and 1403 deletions

View file

@ -1,23 +0,0 @@
name: Lint
on: [push, pull_request]
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v12
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install pnpm
run: curl -L https://unpkg.com/@pnpm/self-installer | node
- name: Install dependencies
run: pnpm i
- name: Run Lint
uses: icrawl/action-eslint@v1

23
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: Testing
on: [push, pull_request]
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v14
uses: actions/setup-node@v2
with:
node-version: 14
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run TSC
run: npm run build

View file

@ -1,23 +0,0 @@
name: TSC
on: [push, pull_request]
jobs:
tsc:
name: TSC
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node v12
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install pnpm
run: curl -L https://unpkg.com/@pnpm/self-installer | node
- name: Install dependencies
run: pnpm i
- name: Run TSC
uses: icrawl/action-tsc@v1

8
.prettierrc.json Normal file
View file

@ -0,0 +1,8 @@
{
"printWidth": 120,
"useTabs": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "all",
"endOfLine": "lf"
}

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017-2020 iCrawl
Copyright (c) 2017-2021 iCrawl
Copyright (c) 2017-2019 Khinenw
Permission is hereby granted, free of charge, to any person obtaining a copy

View file

@ -1,4 +1,5 @@
# Discord Presence
> Update your discord status with the newly added rich presence.
<div align="center">
@ -14,19 +15,20 @@
## Features
* Shows what you are editing in VSCode with no bullsh*t involved
* Support for over 130 of the most popular languages
* Enable/Disable Rich Presence for individual workspaces (enabled by default)
* Custom string support
* Respects Discords 15sec limit when it comes to updating your status
* Stable or Insiders build detection
* Debug mode detection
* Easily manually reconnect to Discord
* VSCode Live Share support
- Shows what you are editing in VSCode with no bullsh\*t involved
- Support for over 130 of the most popular languages
- Enable/Disable Rich Presence for individual workspaces (enabled by default)
- Custom string support
- Respects Discords 15sec limit when it comes to updating your status
- Stable or Insiders build detection
- Debug mode detection
- Easily manually reconnect to Discord
- VSCode Live Share support
## Troubleshooting
### Can't connect to Discord? Check those:
https://github.com/iCrawl/discord-vscode/issues/77#issuecomment-435622205
https://github.com/iCrawl/discord-vscode/issues/85#issuecomment-417895483

3291
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
{
"name": "discord-vscode",
"displayName": "Discord Presence",
"version": "4.1.0",
"version": "5.0.0",
"description": "Update your discord status with the newly added rich presence.",
"private": true,
"author": {
@ -20,7 +20,6 @@
"scripts": {
"prebuild": "npm run lint",
"build": "webpack --mode production",
"tsc": "tsc",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix"
},
@ -34,42 +33,22 @@
"commands": [
{
"command": "discord.enable",
"title": "Enable Discord Presence in the Current Workspace",
"title": "Enable Discord Presence in the current workspace",
"category": "Discord Presence"
},
{
"command": "discord.disable",
"title": "Disable Discord Presence in the Current Workspace",
"title": "Disable Discord Presence in the current workspace",
"category": "Discord Presence"
},
{
"command": "discord.reconnect",
"title": "Reconnect Discord Presence to Discord RPC",
"title": "Reconnect Discord Presence to Discord",
"category": "Discord Presence"
},
{
"command": "discord.disconnect",
"title": "Disconnect Discord Presence from Discord RPC",
"category": "Discord Presence"
},
{
"command": "discord.allowSpectate",
"title": "Allow Spectating",
"category": "Discord Presence"
},
{
"command": "discord.disableSpectate",
"title": "Disable Spectating",
"category": "Discord Presence"
},
{
"command": "discord.allowJoinRequests",
"title": "Allow Join Requests",
"category": "Discord Presence"
},
{
"command": "discord.disableJoinRequests",
"title": "Disable Join Requests",
"title": "Disconnect Discord Presence from Discord",
"category": "Discord Presence"
}
],
@ -83,67 +62,67 @@
"default": true,
"description": "Controls if the Discord Presence should show across all workspaces"
},
"discord.detailsEditing": {
"type": "string",
"default": "Editing {filename}",
"description": "Custom string for the details section of the rich presence\n\t- '{null}' will be replaced with an empty space.\n\t- '{filename}' will be replaced with the current file name.\n\t- '{dirname}' will get replaced with the folder name that has the current file.\n\t- '{fulldirname}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspaceFolder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspaceAndFolder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{currentcolumn}' will get replaced with the current column of the current line.\n\t- '{currentline}' will get replaced with the current line number.\n\t- '{totallines}' will get replaced with the total line number.\n\t- '{filesize}' will get replaced with the current file's size.\n\t- '{gitreponame}' will be replaced with the active Git repository name (from the git URL)\n\t- '{gitbranch}' will be replaced with the current active branch name."
},
"discord.detailsDebugging": {
"type": "string",
"default": "Debugging {filename}",
"description": "Custom string for the details section of the rich presence when debugging\n\t- '{null}' will be replaced with an empty space.\n\t- '{filename}' will be replaced with the current file name.\n\t- '{dirname}' will get replaced with the folder name that has the current file.\n\t- '{fulldirname}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspaceFolder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspaceAndFolder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{currentcolumn}' will get replaced with the current column of the current line.\n\t- '{currentline}' will get replaced with the current line number.\n\t- '{totallines}' will get replaced with the total line number.\n\t- '{filesize}' will get replaced with the current file's size.\n\t- '{gitreponame}' will be replaced with the active Git repository name (from the git URL)\n\t- '{gitbranch}' will be replaced with the current active branch name."
},
"discord.detailsIdle": {
"discord.details_idling": {
"type": "string",
"default": "Idling",
"description": "Custom string for the details section of the rich presence when idling\n\t- '{null}' will be replaced with an empty space."
"description": "Custom string for the details section of the rich presence when idling\n\t- '{empty}' will be replaced with an empty space."
},
"discord.lowerDetailsEditing": {
"discord.details_editing": {
"type": "string",
"default": "Editing {file_name}",
"description": "Custom string for the details section of the rich presence\n\t- '{empty}' will be replaced with an empty space.\n\t- '{file_name}' will be replaced with the current file name.\n\t- '{dir_name}' will get replaced with the folder name that has the current file.\n\t- '{full_dir_name}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspace_folder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspace_and_folder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{current_column}' will get replaced with the current column of the current line.\n\t- '{current_line}' will get replaced with the current line number.\n\t- '{total_lines}' will get replaced with the total line number.\n\t- '{file_size}' will get replaced with the current file's size.\n\t- '{git_repo_name}' will be replaced with the active Git repository name (from the git URL)\n\t- '{git_branch}' will be replaced with the current active branch name."
},
"discord.details_debugging": {
"type": "string",
"default": "Debugging {file_name}",
"description": "Custom string for the details section of the rich presence when debugging\n\t- '{empty}' will be replaced with an empty space.\n\t- '{file_name}' will be replaced with the current file name.\n\t- '{dir_name}' will get replaced with the folder name that has the current file.\n\t- '{full_dir_name}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspace_folder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspace_and_folder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{current_column}' will get replaced with the current column of the current line.\n\t- '{current_line}' will get replaced with the current line number.\n\t- '{total_lines}' will get replaced with the total line number.\n\t- '{file_size}' will get replaced with the current file's size.\n\t- '{git_repo_name}' will be replaced with the active Git repository name (from the git URL)\n\t- '{git_branch}' will be replaced with the current active branch name."
},
"discord.lower_details_idling": {
"type": "string",
"default": "Idling",
"description": "Custom string for the state section of the rich presence when idling\n\t- '{empty}' will be replaced with an empty space."
},
"discord.lower_details_editing": {
"type": "string",
"default": "Workspace: {workspace}",
"description": "Custom string for the state section of the rich presence\n\t- '{null}' will be replaced with an empty space.\n\t- '{filename}' will be replaced with the current file name.\n\t- '{dirname}' will get replaced with the folder name that has the current file.\n\t- '{fulldirname}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspaceFolder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspaceAndFolder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{currentcolumn}' will get replaced with the current column of the current line.\n\t- '{currentline}' will get replaced with the current line number.\n\t- '{totallines}' will get replaced with the total line number.\n\t- '{filesize}' will get replaced with the current file's size.\n\t- '{gitreponame}' will be replaced with the active Git repository name (from the git URL)\n\t- '{gitbranch}' will be replaced with the current active branch name."
"description": "Custom string for the state section of the rich presence\n\t- '{empty}' will be replaced with an empty space.\n\t- '{file_name}' will be replaced with the current file name.\n\t- '{dir_name}' will get replaced with the folder name that has the current file.\n\t- '{full_dir_name}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspace_folder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspace_and_folder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{current_column}' will get replaced with the current column of the current line.\n\t- '{current_line}' will get replaced with the current line number.\n\t- '{total_lines}' will get replaced with the total line number.\n\t- '{file_size}' will get replaced with the current file's size.\n\t- '{git_repo_name}' will be replaced with the active Git repository name (from the git URL)\n\t- '{git_branch}' will be replaced with the current active branch name."
},
"discord.lowerDetailsDebugging": {
"discord.lower_details_debugging": {
"type": "string",
"default": "Debugging: {workspace}",
"description": "Custom string for the state section of the rich presence when debugging\n\t- '{null}' will be replaced with an empty space.\n\t- '{filename}' will be replaced with the current file name.\n\t- '{dirname}' will get replaced with the folder name that has the current file.\n\t- '{fulldirname}' will get replaced with the full directory name without the current file name..\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspaceFolder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspaceAndFolder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{currentcolumn}' will get replaced with the current column of the current line.\n\t- '{currentline}' will get replaced with the current line number.\n\t- '{totallines}' will get replaced with the total line number.\n\t- '{filesize}' will get replaced with the current file's size.\n\t- '{gitreponame}' will be replaced with the active Git repository name (from the git URL)\n\t- '{gitbranch}' will be replaced with the current active branch name."
"description": "Custom string for the state section of the rich presence when debugging\n\t- '{empty}' will be replaced with an empty space.\n\t- '{file_name}' will be replaced with the current file name.\n\t- '{dir_name}' will get replaced with the folder name that has the current file.\n\t- '{full_dir_name}' will get replaced with the full directory name without the current file name.\n\t- '{workspace}' will be replaced with the current workspace name, if any.\n\t- '{workspace_folder}' will be replaced with the currently accessed workspace folder, if any.\n\t- '{workspace_and_folder} will be replaced with the currently accessed workspace and workspace folder like this: 'Workspace - WorkspaceFolder'\n\t- '{current_column}' will get replaced with the current column of the current line.\n\t- '{current_line}' will get replaced with the current line number.\n\t- '{total_lines}' will get replaced with the total line number.\n\t- '{file_size}' will get replaced with the current file's size.\n\t- '{git_repo_name}' will be replaced with the active Git repository name (from the git URL)\n\t- '{git_branch}' will be replaced with the current active branch name."
},
"discord.lowerDetailsIdle": {
"type": "string",
"default": "Idling",
"description": "Custom string for the state section of the rich presence when idling\n\t- '{null}' will be replaced with an empty space."
},
"discord.lowerDetailsNotFound": {
"discord.lower_details_no_workspace_found": {
"type": "string",
"default": "No workspace.",
"description": "Custom string for the state section of the rich presence when no workspace is found.\nIf set to '{null}', this will be an empty space.\n\t- '{currentline}' will get replaced with the current line number.\n\t- '{totallines}' will get replaced with the total line number.\n\t- '{filesize}' will get replaced with the current file's size."
"description": "Custom string for the state section of the rich presence when no workspace is found.\nIf set to '{empty}', this will be an empty space.\n\t- '{current_line}' will get replaced with the current line number.\n\t- '{total_lines}' will get replaced with the total line number.\n\t- '{file_size}' will get replaced with the current file's size."
},
"discord.largeImage": {
"type": "string",
"default": "Editing a {LANG} file",
"description": "Custom string for the largeImageText section of the rich presence.\n\t- '{lang}' will be replaced with the lowercased language ID\n\t- '{LANG}' will be replaced with the uppercased language ID"
},
"discord.largeImageIdle": {
"discord.large_image_idling": {
"type": "string",
"default": "Idling",
"description": "Custom string for the largeImageText section of the rich presence when idling"
},
"discord.smallImage": {
"discord.large_image": {
"type": "string",
"default": "{appname}",
"description": "Custom string for the smallImageText section of the rich presence\n\t- '{appname}' will get replaced with the current Visual Studio Code version."
"default": "Editing a {LANG} file",
"description": "Custom string for the largeImageText section of the rich presence.\n\t- '{lang}' will be replaced with the lowercased language ID\n\t- '{LANG}' will be replaced with the uppercased language ID"
},
"discord.silent": {
"discord.small_image": {
"type": "string",
"default": "{app_name}",
"description": "Custom string for the smallImageText section of the rich presence\n\t- '{app_name}' will get replaced with the current Visual Studio Code version."
},
"discord.suppress_notifications": {
"type": "boolean",
"default": false,
"description": "Decides if error messages are shown to the user"
},
"discord.workspaceElapsedTime": {
"discord.workspace_elapsed_time": {
"type": "boolean",
"default": false,
"default": true,
"description": "Decides whether to display elapsed time for a workspace or a single file"
},
"discord.workspaceExcludePatterns": {
"discord.workspace_exclude_patterns": {
"type": "array",
"items": {
"type": "string"
@ -181,10 +160,10 @@
},
"dependencies": {
"bufferutil": "^4.0.3",
"dayjs": "^1.10.4",
"discord-rpc": "github:discordjs/RPC",
"tslib": "^2.1.0",
"utf-8-validate": "^5.0.4",
"vsls": "^1.0.3015"
"utf-8-validate": "^5.0.4"
},
"devDependencies": {
"@types/node": "^14.14.25",
@ -193,7 +172,7 @@
"@typescript-eslint/parser": "^4.15.0",
"clean-webpack-plugin": "^3.0.0",
"eslint": "^7.19.0",
"eslint-config-marine": "^7.2.0",
"eslint-config-marine": "^8.1.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-prettier": "^3.3.1",
"prettier": "^2.2.1",
@ -204,6 +183,6 @@
"webpack-cli": "^4.5.0"
},
"engines": {
"vscode": "^1.51.0"
"vscode": "^1.53.0"
}
}

193
src/activity.ts Normal file
View file

@ -0,0 +1,193 @@
import { basename, parse, sep } from 'path';
import { debug, env, extensions, Selection, TextDocument, window, workspace } from 'vscode';
import {
CONFIG_KEYS,
DEBUG_IMAGE_KEY,
EMPTY,
FILE_SIZES,
IDLE_IMAGE_KEY,
REPLACE_KEYS,
VSCODE_IMAGE_KEY,
VSCODE_INSIDERS_IMAGE_KEY,
} from './constants';
import { GitExtension } from './git';
import { log, LogLevel } from './logger';
import { getConfig, resolveFileIcon, toLower, toTitle, toUpper } from './util';
interface ActivityPayload {
details: string;
state?: string;
startTimestamp?: number | null;
largeImageKey?: string;
largeImageText?: string;
smallImageKey?: string;
smallImageText?: string;
partyId?: string;
partySize?: number;
partyMax?: number;
matchSecret?: string;
joinSecret?: string;
spectateSecret?: string;
instance?: boolean;
}
export async function activity() {
const config = getConfig();
const appName = env.appName;
const defaultSmallImageKey = debug.activeDebugSession
? DEBUG_IMAGE_KEY
: appName.includes('Insiders')
? VSCODE_INSIDERS_IMAGE_KEY
: VSCODE_IMAGE_KEY;
let state: ActivityPayload = {
details: await details(CONFIG_KEYS.DetailsIdling, CONFIG_KEYS.DetailsEditing, CONFIG_KEYS.DetailsDebugging),
state: await details(
CONFIG_KEYS.LowerDetailsIdling,
CONFIG_KEYS.LowerDetailsEditing,
CONFIG_KEYS.LowerDetailsDebugging,
),
startTimestamp: null,
largeImageKey: IDLE_IMAGE_KEY,
largeImageText: config[CONFIG_KEYS.LargeImageIdling],
smallImageKey: defaultSmallImageKey,
smallImageText: config[CONFIG_KEYS.SmallImage].replace(REPLACE_KEYS.AppName, appName),
};
if (window.activeTextEditor) {
const largeImageKey = resolveFileIcon(window.activeTextEditor.document);
const largeImageText = config[CONFIG_KEYS.LargeImage]
.replace(REPLACE_KEYS.LanguageLowerCase, toLower(largeImageKey))
.replace(REPLACE_KEYS.LanguageTitleCase, toTitle(largeImageKey))
.replace(REPLACE_KEYS.LanguageUpperCase, toUpper(largeImageKey))
.padEnd(2, EMPTY);
state = {
...state,
details: await details(CONFIG_KEYS.DetailsIdling, CONFIG_KEYS.DetailsEditing, CONFIG_KEYS.DetailsDebugging),
state: await details(
CONFIG_KEYS.LowerDetailsIdling,
CONFIG_KEYS.LowerDetailsEditing,
CONFIG_KEYS.LowerDetailsDebugging,
),
largeImageKey,
largeImageText,
};
}
log(LogLevel.Debug, JSON.stringify(state, null, 2));
return state;
}
async function details(idling: CONFIG_KEYS, editing: CONFIG_KEYS, debugging: CONFIG_KEYS) {
const config = getConfig();
let raw = (config[idling] as string).replace(REPLACE_KEYS.Empty, EMPTY);
if (window.activeTextEditor) {
const fileName = basename(window.activeTextEditor.document.fileName);
const { dir } = parse(window.activeTextEditor.document.fileName);
const split = dir.split(sep);
const dirName = split[split.length - 1];
const noWorkspaceFound = config[CONFIG_KEYS.LowerDetailsNoWorkspaceFound].replace(REPLACE_KEYS.Empty, EMPTY);
const workspaceFolder = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri);
const workspaceFolderName = workspaceFolder?.name ?? noWorkspaceFound;
const workspaceName = workspace.name ?? workspaceFolderName;
const workspaceAndFolder = `${workspaceName}${workspaceFolderName === EMPTY ? '' : ` - ${workspaceFolderName}`}`;
const fileIcon = resolveFileIcon(window.activeTextEditor.document);
if (debug.activeDebugSession) {
raw = config[debugging] as string;
} else {
raw = config[editing] as string;
}
if (workspaceFolder) {
const { name } = workspaceFolder;
const relativePath = workspace.asRelativePath(window.activeTextEditor.document.fileName).split(sep);
relativePath.splice(-1, 1);
raw = raw.replace(REPLACE_KEYS.FullDirName, `${name}${sep}${relativePath.join(sep)}`);
}
raw = await fileDetails(raw, window.activeTextEditor.document, window.activeTextEditor.selection);
raw = raw
.replace(REPLACE_KEYS.FileName, fileName)
.replace(REPLACE_KEYS.DirName, dirName)
.replace(REPLACE_KEYS.Workspace, workspaceName)
.replace(REPLACE_KEYS.WorkspaceFolder, workspaceFolderName)
.replace(REPLACE_KEYS.WorkspaceAndFolder, workspaceAndFolder)
.replace(REPLACE_KEYS.LanguageLowerCase, toLower(fileIcon))
.replace(REPLACE_KEYS.LanguageTitleCase, toTitle(fileIcon))
.replace(REPLACE_KEYS.LanguageUpperCase, toUpper(fileIcon));
}
return raw;
}
async function fileDetails(_raw: string, document: TextDocument, selection: Selection) {
let raw = _raw.slice();
const gitExtension = extensions.getExtension<GitExtension>('vscode.git');
const git = gitExtension?.exports.getAPI(1);
if (raw.includes(REPLACE_KEYS.TotalLines)) {
raw = raw.replace(REPLACE_KEYS.TotalLines, document.toLocaleString());
}
if (raw.includes(REPLACE_KEYS.CurrentLine)) {
raw = raw.replace(REPLACE_KEYS.CurrentLine, (selection.active.line + 1).toLocaleString());
}
if (raw.includes(REPLACE_KEYS.CurrentColumn)) {
raw = raw.replace(REPLACE_KEYS.CurrentColumn, (selection.active.character + 1).toLocaleString());
}
if (raw.includes(REPLACE_KEYS.FileSize)) {
let currentDivision = 0;
let { size } = await workspace.fs.stat(document.uri);
const originalSize = size;
if (originalSize > 1000) {
size /= 1000;
currentDivision++;
while (size > 1000) {
currentDivision++;
size /= 1000;
}
}
raw = raw.replace(
REPLACE_KEYS.FileSize,
`${originalSize > 1000 ? size.toFixed(2) : size}${FILE_SIZES[currentDivision]}`,
);
}
if (raw.includes(REPLACE_KEYS.GitBranch)) {
if (git?.repositories.length) {
raw = raw.replace(
REPLACE_KEYS.GitBranch,
git.repositories.find((repo) => repo.ui.selected)?.state.HEAD?.name ?? EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitBranch, 'Unknown');
}
}
if (raw.includes(REPLACE_KEYS.GitRepoName)) {
if (git?.repositories.length) {
raw = raw.replace(
REPLACE_KEYS.GitRepoName,
git.repositories
.find((repo) => repo.ui.selected)
?.state.remotes[0].fetchUrl?.split('/')[1]
.replace('.git', '') ?? EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitRepoName, 'Unknown');
}
}
return raw;
}

View file

@ -1,172 +0,0 @@
const { Client } = require('discord-rpc'); // eslint-disable-line
import { Disposable, StatusBarItem, Uri, window, workspace, env } from 'vscode';
import * as vsls from 'vsls';
import Activity from '../structures/Activity';
import Logger from '../structures/Logger';
import { API } from '../git';
let activityTimer: NodeJS.Timer | undefined;
export default class RPCClient implements Disposable {
public config = workspace.getConfiguration('discord');
public git?: API;
private rpc: any;
private readonly activity = new Activity(this);
public constructor(private readonly clientId: string, public statusBarIcon: StatusBarItem) {}
public get client() {
return this.rpc;
}
public async setActivity(workspaceElapsedTime = false) {
if (!this.rpc) return;
const activity = await this.activity.generate(workspaceElapsedTime);
if (!activity) return;
Logger.log('Sending activity to Discord.');
this.rpc.setActivity(activity);
}
public allowSpectate() {
if (!this.rpc) return;
Logger.log('Allowed spectating.');
Logger.log('Sending spectate activity to Discord.');
void this.activity.allowSpectate();
}
public disableSpectate() {
if (!this.rpc) return;
Logger.log('Disabled spectating.');
void this.activity.disableSpectate();
}
public allowJoinRequests() {
if (!this.rpc) return;
Logger.log('Allowed join requests.');
Logger.log('Sending join activity to Discord.');
void this.activity.allowJoinRequests();
}
public disableJoinRequests() {
if (!this.rpc) return;
Logger.log('Disabled join requests.');
void this.activity.disableJoinRequests();
}
public async login() {
if (this.rpc) {
this.dispose();
}
this.rpc = new Client({ transport: 'ipc' });
Logger.log('Logging into RPC...');
this.rpc.transport.once('close', () => {
if (!this.config.get<boolean>('enabled')) return;
void this.dispose();
this.statusBarIcon.text = '$(plug) Reconnect to Discord';
this.statusBarIcon.command = 'discord.reconnect';
this.statusBarIcon.tooltip = '';
});
this.rpc.once('ready', async () => {
Logger.log('Successfully connected to Discord.');
this.statusBarIcon.text = '$(globe) Connected to Discord';
this.statusBarIcon.tooltip = 'Connected to Discord';
setTimeout(() => (this.statusBarIcon.text = '$(globe)'), 5000);
if (activityTimer) clearInterval(activityTimer);
void this.setActivity(this.config.get<boolean>('workspaceElapsedTime'));
activityTimer = setInterval(() => {
this.config = workspace.getConfiguration('discord');
void this.setActivity(this.config.get<boolean>('workspaceElapsedTime'));
}, 1000);
this.rpc.subscribe('ACTIVITY_SPECTATE', async ({ secret }: { secret: string }) => {
const liveshare = await vsls.getApi();
if (!liveshare) return;
try {
const s = Buffer.from(secret, 'base64').toString();
// You might be asking yourself: "but why?"
// VS Liveshare has this annoying bug where you convert a URL string to a URI object to autofill
// But the autofill will be empty, so to circumvent this I need to add copying the link to the clipboard
// And immediately pasting it after the window pops up empty
await env.clipboard.writeText(s);
const uriString = await env.clipboard.readText();
const uri = Uri.parse(uriString);
await liveshare.join(uri);
} catch (error) {
Logger.log(error);
}
});
// You might be asking yourself again: "but why?"
// Same here, this is a real nasty race condition that happens inside the discord-rpc module currently
// To circumvent this we need to timeout sending the subscribe events to the discord client
setTimeout(() => {
this.rpc.subscribe(
'ACTIVITY_JOIN_REQUEST',
async ({ user }: { user: { username: string; discriminator: string } }) => {
const val = await window.showInformationMessage(
`${user.username}#${user.discriminator} wants to join your session`,
{ title: 'Accept' },
{ title: 'Decline' },
);
if (val && val.title === 'Accept') await this.rpc.sendJoinInvite(user);
else await this.rpc.closeJoinRequest(user);
},
);
}, 1000);
setTimeout(() => {
this.rpc.subscribe('ACTIVITY_JOIN', async ({ secret }: { secret: string }) => {
const liveshare = await vsls.getApi();
if (!liveshare) return;
try {
const s = Buffer.from(secret, 'base64').toString();
// You might be asking yourself again again: "but why?"
// See first comment above
await env.clipboard.writeText(s);
const uriString = await env.clipboard.readText();
const uri = Uri.parse(uriString);
await liveshare.join(uri);
} catch (error) {
Logger.log(error);
}
});
}, 2000);
const liveshare = await vsls.getApi();
if (!liveshare) return;
liveshare.onDidChangeSession(({ session }) => {
if (session.id) return this.activity.changePartyId(session.id);
return this.activity.changePartyId();
});
liveshare.onDidChangePeers(({ added, removed }) => {
if (added.length) return this.activity.increasePartySize(added.length);
else if (removed.length) return this.activity.decreasePartySize(removed.length);
});
});
try {
await this.rpc.login({ clientId: this.clientId });
} catch (error) {
throw error;
}
}
public dispose() {
this.activity.dispose();
if (this.rpc) this.rpc.destroy();
this.rpc = null;
this.statusBarIcon.tooltip = '';
if (activityTimer) clearInterval(activityTimer);
}
}

View file

@ -1,8 +1,51 @@
export const LIVE_SHARE_BASE_URL = 'insiders.liveshare.vsengsaas.visualstudio.com';
export const VSLS_EXTENSION_ID = 'ms-vsliveshare.vsliveshare';
import LANG from './data/languages.json';
export const LIVE_SHARE_COMMANDS = {
Start: 'liveshare.start',
End: 'liveshare.end',
Join: 'liveshare.join',
};
export const CLIENT_ID = '383226320970055681' as const;
export const KNOWN_EXTENSIONS: { [key: string]: { image: string } } = LANG.KNOWN_EXTENSIONS;
export const KNOWN_LANGUAGES: { language: string; image: string }[] = LANG.KNOWN_LANGUAGES;
export const EMPTY = '\u200b\u200b';
export const FILE_SIZES = [' bytes', 'kb', 'mb', 'gb', 'tb'];
export const IDLE_IMAGE_KEY = 'vscode-big';
export const DEBUG_IMAGE_KEY = 'debug';
export const VSCODE_IMAGE_KEY = 'vscode';
export const VSCODE_INSIDERS_IMAGE_KEY = 'vscode-insiders';
export const enum REPLACE_KEYS {
Empty = '{empty}',
FileName = '{file_name}',
DirName = '{dir_name}',
FullDirName = '{full_dir_name}',
Workspace = '{workspace}',
WorkspaceFolder = '{workspace_folder}',
WorkspaceAndFolder = '{workspace_and_folder}',
LanguageLowerCase = '{lang}',
LanguageTitleCase = '{Lang}',
LanguageUpperCase = '{LANG}',
TotalLines = '{total_lines}',
CurrentLine = '{current_line}',
CurrentColumn = '{current_column}',
FileSize = '{file_size}',
AppName = '{app_name}',
GitRepoName = '{git_repo_name}',
GitBranch = '{git_branch}',
}
export const enum CONFIG_KEYS {
Enabled = 'enabled',
DetailsIdling = 'details_idling',
DetailsEditing = 'details_editing',
DetailsDebugging = 'details_debugging',
LowerDetailsIdling = 'lower_details_idling',
LowerDetailsEditing = 'lower_details_editing',
LowerDetailsDebugging = 'lower_details_debugging',
LowerDetailsNoWorkspaceFound = 'lower_details_no_workspace_found',
LargeImageIdling = 'large_image_idling',
LargeImage = 'large_image',
SmallImage = 'small_image',
SuppressNotifications = 'suppress_notifications',
WorkspaceElapsedTime = 'workspace_elapsed_time',
WorkspaceExcludePatterns = 'workspace_exclude_patterns',
}

View file

@ -1,520 +1,445 @@
{
"knownLanguages": [
"ahk",
"android",
"angular",
"applescript",
"appveyor",
"arduino",
"as",
"asp",
"assembly",
"autoit",
"babel",
"bat",
"bower",
"brainfuck",
"c",
"cargo",
"cfml",
"circleci",
"clojure",
"cmake",
"codeclimate",
"coffee",
"cpp",
"crystal",
"csharp",
"cshtml",
"css",
"cuda",
"d",
"dart",
"delphi",
"denizen",
"dm",
"docker",
"editorconfig",
"ejs",
"elixir",
"elm",
"env",
"erlang",
"eslint",
"flowconfig",
"fsharp",
"gemfile",
"git",
"go",
"godot",
"gradle",
"graphql",
"groovy",
"gruntfile",
"gulp",
"handlebars",
"harbour",
"haskell",
"haxe",
"heroku",
"hjson",
"html",
"http",
"ini",
"jar",
"java",
"jest",
"jinja",
"js",
"jsmap",
"json",
"jsx",
"julia",
"jupyter",
"kotlin",
"less",
"lisp",
"livescript",
"log",
"lua",
"makefile",
"manifest",
"markdown",
"markdownx",
"marko",
"nim",
"nix",
"nodemon",
"npm",
"objc",
"ocaml",
"pascal",
"pawn",
"perl",
"php",
"ponylang",
"postcss",
"powershell",
"prettier",
"prisma",
"processing",
"pug",
"purescript",
"python",
"r",
"reasonml",
"ruby",
"rust",
"scala",
"scss",
"shell",
"sqf",
"sql",
"stylus",
"svelte",
"svg",
"swift",
"tex",
"text",
"toml",
"travis",
"ts",
"tsx",
"twig",
"typescript-def",
"v",
"v",
"vala",
"vb",
"vba",
"vbhtml",
"vbproj",
"vcxproj",
"vscodeignore",
"vue",
"wasm",
"webpack",
"xml",
"yaml",
"yarn",
"zig"
],
"knownExtensions": {
".ahk": { "image": "ahk" },
".ahkl": { "image": "ahk" },
"androidmanifest.xml": { "image": "android" },
"/^angular[^.]*\\.js$/i": { "image": "angular" },
".applescript": { "image": "applescript" },
"/(\\.)?appveyor\\.yml/i": { "image": "appveyor" },
".ino": { "image": "arduino" },
".swf": { "image": "as" },
".as": { "image": "as" },
".jsfl": { "image": "as" },
".swc": { "image": "as" },
".asp": { "image": "asp" },
".asax": { "image": "asp" },
".ascx": { "image": "asp" },
".ashx": { "image": "asp" },
".asmx": { "image": "asp" },
".aspx": { "image": "asp" },
".axd": { "image": "asp" },
"/\\.(l?a|[ls]?o|out|s|a51|asm|axf|elf|prx|puff|z80)$/i": { "image": "assembly" },
".agc": { "image": "assembly" },
".ko": { "image": "assembly" },
".lst": { "image": "assembly" },
"/\\.((c([+px]{2}?)?-?)?objdump|bsdiff|bin|dat|pak|pdb)$/i": { "image": "assembly" },
".d-objdump": { "image": "assembly" },
"/\\.gcode|\\.gco/i": { "image": "assembly" },
"/\\.rpy[bc]$/i": { "image": "assembly" },
"/\\.py[co]$/i": { "image": "assembly" },
".swp": { "image": "assembly" },
".DS_Store": { "image": "assembly" },
".au3": { "image": "autoit" },
"babel.config.js": { "image": "babel" },
"/\\.babelrc/i": { "image": "babel" },
".bat": { "image": "bat" },
".batch": { "image": "bat" },
".cmd": { "image": "bat" },
"/\\.(exe|com|msi)$/i": { "image": "bat" },
".reg": { "image": "bat" },
"/^(\\.bowerrc|bower\\.json|Bowerfile)$/i": { "image": "bower" },
"/\\.bf?$/i": { "image": "brainfuck" },
"/\\.c$/i": { "image": "c" },
"/(cargo.toml|cargo.lock)/i": { "image": "cargo" },
".cfc": { "image": "cfml" },
".cfm": { "image": "cfml" },
"circle.yml": { "image": "circleci" },
".clj": { "image": "clojure" },
".cl2": { "image": "clojure" },
".cljc": { "image": "clojure" },
".cljx": { "image": "clojure" },
".hic": { "image": "clojure" },
"/\\.cljs(cm)?$/i": { "image": "clojure" },
".cmake": { "image": "cmake" },
"/^CMakeLists\\.txt$/": { "image": "cmake" },
"/\\.codeclimate\\.(yml|json)/i": { "image": "codeclimate" },
".coffee": { "image": "coffee" },
".cjsx": { "image": "coffee" },
".coffee.ecr": { "image": "coffee" },
".coffee.erb": { "image": "coffee" },
".litcoffee": { "image": "coffee" },
".iced": { "image": "coffee" },
"/\\.c[+px]{2}$|\\.cc$/i": { "image": "cpp" },
"/\\.h[+px]{2}$/i": { "image": "cpp" },
"/\\.[it]pp$/i": { "image": "cpp" },
"/\\.(tcc|inl)$/i": { "image": "cpp" },
".cats": { "image": "cpp" },
".idc": { "image": "cpp" },
".w": { "image": "cpp" },
".nc": { "image": "cpp" },
".upc": { "image": "cpp" },
".xpm": { "image": "cpp" },
"/\\.e?cr$/i": { "image": "crystal" },
".cs": { "image": "csharp" },
".csx": { "image": "csharp" },
".cshtml": { "image": "cshtml" },
".css": { "image": "css" },
".css.map": { "image": "cssmap" },
".cu": { "image": "cuda" },
"/\\.di?$/i": { "image": "d" },
".dart": { "image": "dart" },
".dfm": { "image": "delphi" },
".dpr": { "image": "delphi" },
".dsc": { "image": "denizen" },
".dm": { "image": "dm" },
".dme": { "image": "dm" },
".dmm": { "image": "dm" },
"/^(Dockerfile|docker-compose)|\\.docker(file|ignore)$/i": { "image": "docker" },
"/^docker-sync\\.yml$/i": { "image": "docker" },
".editorconfig": { "image": "editorconfig" },
".ejs": { "image": "ejs" },
".ex": { "image": "elixir" },
"/\\.(exs|l?eex)$/i": { "image": "elixir" },
"/^mix\\.(exs?|lock)$/i": { "image": "elixir" },
".elm": { "image": "elm" },
".env": { "image": "env" },
".erl": { "image": "erlang" },
".beam": { "image": "erlang" },
".hrl": { "image": "erlang" },
".xrl": { "image": "erlang" },
".yrl": { "image": "erlang" },
".app.src": { "image": "erlang" },
"/^Emakefile$/": { "image": "erlang" },
"/^rebar(\\.config)?\\.lock$/i": { "image": "erlang" },
"/\\.(eslintrc|eslintignore)/i": { "image": "eslint" },
"/(\\.firebaserc|firebase\\.json)/i": { "image": "firebase" },
".flowconfig": { "image": "flowconfig" },
".fs": { "image": "fsharp" },
".fsi": { "image": "fsharp" },
".fsscript": { "image": "fsharp" },
".fsx": { "image": "fsharp" },
"/gatsby-(browser|node|ssr|config)\\.js/i": { "image": "gatsbyjs" },
"/^Gemfile(\\.lock)?$/i": { "image": "gemfile" },
"/^\\.git|^\\.keep$|\\.mailmap$/i": { "image": "git" },
".go": { "image": "go" },
".gd": { "image": "godot" },
".gradle": { "image": "gradle" },
"gradlew": { "image": "gradle" },
".gql": { "image": "graphql" },
".graphql": { "image": "graphql" },
".groovy": { "image": "groovy" },
".gvy": { "image": "groovy" },
".gy": { "image": "groovy" },
".gsh": { "image": "groovy" },
"/gruntfile\\.(js|coffee)/i": { "image": "gruntfile" },
"gulpfile.js": { "image": "gulp" },
"/\\.(hbs|handlebars|(mu)?stache)$/i": { "image": "handlebars" },
".prg": { "image": "harbour" },
".hbp": { "image": "harbour" },
".hbc": { "image": "harbour" },
".rc": { "image": "harbour" },
".fmg": { "image": "harbour" },
".hs": { "image": "haskell" },
".hsc": { "image": "haskell" },
".c2hs": { "image": "haskell" },
".lhs": { "image": "haskell" },
".hx": { "image": "haxe" },
".hxml": { "image": "haxe" },
"/^procfile/i": { "image": "heroku" },
"heroku.yml": { "image": "heroku" },
".hjson": { "image": "hjson" },
"/\\.x?html?$/i": { "image": "html" },
".http": { "image": "http" },
".rest": { "image": "http" },
".jar": { "image": "jar" },
".java": { "image": "java" },
"jest.config.js": { "image": "jest" },
".jinja": { "image": "jinja" },
".js": { "image": "js" },
".es6": { "image": "js" },
".es": { "image": "js" },
".mjs": { "image": "js" },
".js.map": { "image": "jsmap" },
".json": { "image": "json" },
".jsonc": { "image": "json" },
".jsx": { "image": "jsx" },
"/\\.(jil|jl)/i": { "image": "julia" },
".ipynb": { "image": "jupyter" },
".kt": { "image": "kotlin" },
".ktm": { "image": "kotlin" },
".kts": { "image": "kotlin" },
".less": { "image": "less" },
".lsp": { "image": "lisp" },
".lisp": { "image": "lisp" },
".l": { "image": "lisp" },
".nl": { "image": "lisp" },
".ny": { "image": "lisp" },
".podsl": { "image": "lisp" },
".sexp": { "image": "lisp" },
".ls": { "image": "livescript" },
".log": { "image": "log" },
".lua": { "image": "lua" },
".pd_lua": { "image": "lua" },
".rbxs": { "image": "lua" },
".wlua": { "image": "lua" },
"/^Makefile/": { "image": "makefile" },
"/^mk\\.config$/": { "image": "makefile" },
"/\\.(mk|mak|make)$/i": { "image": "makefile" },
"/^BSDmakefile$/i": { "image": "makefile" },
"/^GNUmakefile$/i": { "image": "makefile" },
"/^makefile\\.sco$/i": { "image": "makefile" },
"/^Kbuild$/": { "image": "makefile" },
"/^makefile$/": { "image": "makefile" },
"/^mkfile$/i": { "image": "makefile" },
"/^\\.?qmake$/i": { "image": "makefile" },
"/\\.(h|geo|topo)$/i": { "image": "manifest" },
".cson": { "image": "manifest" },
".json5": { "image": "manifest" },
".ndjson": { "image": "manifest" },
".fea": { "image": "manifest" },
".json.eex": { "image": "manifest" },
".proto": { "image": "manifest" },
".pytb": { "image": "manifest" },
".pydeps": { "image": "manifest" },
"/\\.pot?$/i": { "image": "manifest" },
".ejson": { "image": "manifest" },
".edn": { "image": "manifest" },
".eam.fs": { "image": "manifest" },
".qml": { "image": "manifest" },
".qbs": { "image": "manifest" },
".ston": { "image": "manifest" },
".ttl": { "image": "manifest" },
".rviz": { "image": "manifest" },
".syntax": { "image": "manifest" },
".webmanifest": { "image": "manifest" },
"/^pkginfo$/": { "image": "manifest" },
"/^mime\\.types$/i": { "image": "manifest" },
"/^METADATA\\.pb$/": { "image": "manifest" },
"/[\\/\\\\](?:magic[\\/\\\\]Magdir|file[\\/\\\\]magic)[\\/\\\\][-.\\w]+$/i": { "image": "manifest" },
"/(\\\\|\\/)dev[-\\w]+\\1(?:[^\\\\\\/]+\\1)*(?!DESC|NOTES)(?:[A-Z][-A-Z]*)(?:\\.in)?$/": { "image": "manifest" },
"lib/icons/.icondb.js": { "image": "manifest" },
"/\\.git[\\/\\\\](.*[\\/\\\\])?(HEAD|ORIG_HEAD|packed-refs|logs[\\/\\\\](.+[\\/\\\\])?[^\\/\\\\]+)$/": {
"image": "manifest"
},
"/\\.(md|mdown|markdown|mkd|mkdown|mdwn|mkdn|rmd|ron|pmd)$/i": { "image": "markdown" },
".mdx": { "image": "markdownx" },
".marko": { "image": "marko" },
".nim": { "image": "nim" },
".nims": { "image": "nim" },
".nimble": { "image": "nim" },
".nix": { "image": "nix" },
"nodemon.json": { "image": "nodemon" },
".npmrc": { "image": "npm" },
"/\\.mm?$/i": { "image": "objc" },
".pch": { "image": "objc" },
".x": { "image": "objc" },
".ml": { "image": "ocaml" },
".mli": { "image": "ocaml" },
".eliom": { "image": "ocaml" },
".eliomi": { "image": "ocaml" },
".ml4": { "image": "ocaml" },
".mll": { "image": "ocaml" },
".mly": { "image": "ocaml" },
"/\\.pas(cal)?$/i": { "image": "pascal" },
".lpr": { "image": "pascal" },
".p": { "image": "pawn" },
".inc": { "image": "pawn" },
".sma": { "image": "pawn" },
".pwn": { "image": "pawn" },
".sp": { "image": "pawn" },
"/\\.p(er)?l$/i": { "image": "perl" },
".al": { "image": "perl" },
".ph": { "image": "perl" },
".plx": { "image": "perl" },
".pm": { "image": "perl" },
"/\\.(psgi|xs)$/i": { "image": "perl" },
".pl6": { "image": "perl" },
"/\\.[tp]6$|\\.6pl$/i": { "image": "perl" },
"/\\.(pm6|p6m)$/i": { "image": "perl" },
".6pm": { "image": "perl" },
".nqp": { "image": "perl" },
".p6l": { "image": "perl" },
".pod6": { "image": "perl" },
"/^Rexfile$/": { "image": "perl" },
"/\\.php([st\\d]|_cs)?$/i": { "image": "php" },
"/^Phakefile/": { "image": "php" },
".pony": { "image": "ponylang" },
".pcss": { "image": "postcss" },
".ps1": { "image": "powershell" },
".psd1": { "image": "powershell" },
".psm1": { "image": "powershell" },
".ps1xml": { "image": "powershell" },
".prettierignore": { "image": "prettier" },
"/\\.prettier((rc)|(\\.(toml|yml|yaml|json|js))?$){2}/i": { "image": "prettier" },
"prettier.config.js": { "image": "prettier" },
"prisma.yml": { "image": "prisma" },
".pde": { "image": "processing" },
".jade": { "image": "pug" },
".pug": { "image": "pug" },
".purs": { "image": "purescript" },
".py": { "image": "python" },
".ipy": { "image": "python" },
".isolate": { "image": "python" },
".pep": { "image": "python" },
".gyp": { "image": "python" },
".gypi": { "image": "python" },
".pyde": { "image": "python" },
".pyp": { "image": "python" },
".pyt": { "image": "python" },
".py3": { "image": "python" },
".pyi": { "image": "python" },
".pyw": { "image": "python" },
".tac": { "image": "python" },
".wsgi": { "image": "python" },
".xpy": { "image": "python" },
".rpy": { "image": "python" },
"/\\.?(pypirc|pythonrc|python-venv)$/i": { "image": "python" },
"/^(SConstruct|SConscript)$/": { "image": "python" },
"/^(Snakefile|WATCHLISTS)$/": { "image": "python" },
"/^wscript$/": { "image": "python" },
"/\\.(r|Rprofile|rsx|rd)$/i": { "image": "r" },
".re": { "image": "reasonml" },
"/\\.(rb|ru|ruby|erb|gemspec|god|mspec|pluginspec|podspec|rabl|rake|opal)$/i": { "image": "ruby" },
"/^\\.?(irbrc|gemrc|pryrc|ruby-(gemset|version))$/i": { "image": "ruby" },
"/^(Appraisals|(Rake|[bB]uild|Cap|Danger|Deliver|Fast|Guard|Jar|Maven|Pod|Puppet|Snap)file(\\.lock)?)$/": {
"image": "ruby"
},
"/\\.(jbuilder|rbuild|rb[wx]|builder)$/i": { "image": "ruby" },
"/^rails$/": { "image": "ruby" },
".watchr": { "image": "ruby" },
".rs": { "image": "rust" },
"/\\.(sc|scala)$/i": { "image": "scala" },
".scss": { "image": "scss" },
".sass": { "image": "scss" },
"/\\.(sh|rc|bats|bash|tool|install|command)$/i": { "image": "shell" },
"/^(\\.?bash(rc|[-_]?(profile|login|logout|history|prompt))|_osc|config|install-sh|PKGBUILD)$/i": {
"image": "shell"
},
"/\\.(ksh|mksh|pdksh)$/i": { "image": "shell" },
".sh-session": { "image": "shell" },
"/\\.zsh(-theme|_history)?$|^\\.?(antigen|zpreztorc|zlogin|zlogout|zprofile|zshenv|zshrc)$/i": { "image": "shell" },
"/\\.fish$|^\\.fishrc$/i": { "image": "shell" },
"/^\\.?(login|profile)$/": { "image": "shell" },
".inputrc": { "image": "shell" },
".tmux": { "image": "shell" },
"/^(configure|config\\.(guess|rpath|status|sub)|depcomp|libtool|compile)$/": { "image": "shell" },
"/^\\/(private\\/)?etc\\/([^\\/]+\\/)*(profile$|nanorc$|rc\\.|csh\\.)/i": { "image": "shell" },
"/^\\.?cshrc$/i": { "image": "shell" },
".profile": { "image": "shell" },
".tcsh": { "image": "shell" },
".csh": { "image": "shell" },
".sqf": { "image": "sqf" },
"/\\.(my)?sql$/i": { "image": "sql" },
".ddl": { "image": "sql" },
".udf": { "image": "sql" },
".hql": { "image": "sql" },
".viw": { "image": "sql" },
".prc": { "image": "sql" },
".cql": { "image": "sql" },
".db2": { "image": "sql" },
"/\\.(styl|stylus)$/i": { "image": "stylus" },
".svelte": { "image": "svelte" },
".svg": { "image": "svg" },
".swift": { "image": "swift" },
".tex": { "image": "tex" },
".ltx": { "image": "tex" },
".aux": { "image": "tex" },
".sty": { "image": "tex" },
".dtx": { "image": "tex" },
".cls": { "image": "tex" },
".ins": { "image": "tex" },
".lbx": { "image": "tex" },
".mkiv": { "image": "tex" },
".mkvi": { "image": "tex" },
".mkii": { "image": "tex" },
".texi": { "image": "tex" },
"/^hyphen(ex)?\\.(cs|den|det|fr|sv|us)$/": { "image": "tex" },
"/\\.te?xt$/i": { "image": "text" },
".rtf": { "image": "text" },
"/\\.i?nfo$/i": { "image": "text" },
".msg": { "image": "text" },
"/\\.(utxt|utf8)$/i": { "image": "text" },
".toml": { "image": "toml" },
".travis.yml": { "image": "travis" },
".ts": { "image": "ts" },
".tsx": { "image": "tsx" },
".twig": { "image": "twig" },
"/.*\\.d\\.ts/i": { "image": "typescript-def" },
".v": { "image": "v" },
".vh": { "image": "v" },
".vala": { "image": "vala" },
".vapi": { "image": "vala" },
".vb": { "image": "vb" },
".vbs": { "image": "vb" },
".vbhtml": { "image": "vb" },
".vbproj": { "image": "vb" },
".vba": { "image": "vba" },
".vcxproj": { "image": "vcxproj" },
".vscodeignore": { "image": "vscodeignore" },
".vue": { "image": "vue" },
".wat": { "image": "wasm" },
".wast": { "image": "wasm" },
".wasm": { "image": "wasm" },
"/webpack(\\.dev|\\.development|\\.prod|\\.production)?\\.config(\\.babel)?\\.(js|jsx|coffee|ts|json|json5|yaml|yml)/i": { "image": "webpack" },
".xml": { "image": "xml" },
"/\\.ya?ml$/i": { "image": "yaml" },
"/^yarn(\\.lock)?$/i": { "image": "yarn" },
".yarnrc": { "image": "yarn" },
".zig": { "image": "zig" }
}
"KNOWN_LANGUAGES": [
{ "language": "abap", "image": "text" },
{ "language": "bat", "image": "bat" },
{ "language": "bibtex", "image": "text" },
{ "language": "clojure", "image": "clojure" },
{ "language": "coffeescript", "image": "coffeescript" },
{ "language": "c", "image": "c" },
{ "language": "cpp", "image": "cpp" },
{ "language": "csharp", "image": "csharp" },
{ "language": "css", "image": "css" },
{ "language": "diff", "image": "manifest" },
{ "language": "dockerfile", "image": "docker" },
{ "language": "fsharp", "image": "fsharp" },
{ "language": "git-commit", "image": "manifest" },
{ "language": "git-rebase", "image": "manifest" },
{ "language": "go", "image": "go" },
{ "language": "groovy", "image": "groovy" },
{ "language": "handlebars", "image": "handlebars" },
{ "language": "haml", "image": "text" },
{ "language": "html", "image": "html" },
{ "language": "ini", "image": "manifest" },
{ "language": "java", "image": "java" },
{ "language": "javascript", "image": "javascript" },
{ "language": "javascriptreact", "image": "jsx" },
{ "language": "jsx", "image": "jsx" },
{ "language": "json", "image": "json" },
{ "language": "jsonc", "image": "json" },
{ "language": "latex", "image": "text" },
{ "language": "less", "image": "less" },
{ "language": "lua", "image": "lua" },
{ "language": "makefile", "image": "makefile" },
{ "language": "markdown", "image": "markdown" },
{ "language": "objective-c", "image": "objective-c" },
{ "language": "objective-cpp", "image": "objective-c" },
{ "language": "perl", "image": "perl" },
{ "language": "perl6", "image": "perl" },
{ "language": "php", "image": "php" },
{ "language": "plaintext", "image": "text" },
{ "language": "powershell", "image": "powershell" },
{ "language": "jade", "image": "pug" },
{ "language": "pug", "image": "pug" },
{ "language": "python", "image": "python" },
{ "language": "r", "image": "r" },
{ "language": "razor", "image": "cshtml" },
{ "language": "ruby", "image": "ruby" },
{ "language": "rust", "image": "rust" },
{ "language": "scss", "image": "scss" },
{ "language": "sass", "image": "scss" },
{ "language": "shaderlab", "image": "manifest" },
{ "language": "slim", "image": "text" },
{ "language": "sql", "image": "sql" },
{ "language": "stylus", "image": "stylus" },
{ "language": "swift", "image": "swift" },
{ "language": "typescript", "image": "typescript" },
{ "language": "typescriptreact", "image": "tsx" },
{ "language": "tex", "image": "tex" },
{ "language": "vb", "image": "vb" },
{ "language": "vue", "image": "vue" },
{ "language": "vue-html", "image": "vue" },
{ "language": "xml", "image": "xml" },
{ "language": "xsl", "image": "xml" },
{ "language": "yaml", "image": "yaml" }
],
"KNOWN_EXTENSIONS": {
".ahk": { "image": "ahk" },
".ahkl": { "image": "ahk" },
"androidmanifest.xml": { "image": "android" },
"/^angular[^.]*\\.js$/i": { "image": "angular" },
".applescript": { "image": "applescript" },
"/(\\.)?appveyor\\.yml/i": { "image": "appveyor" },
".ino": { "image": "arduino" },
".swf": { "image": "as" },
".as": { "image": "as" },
".jsfl": { "image": "as" },
".swc": { "image": "as" },
".asp": { "image": "asp" },
".asax": { "image": "asp" },
".ascx": { "image": "asp" },
".ashx": { "image": "asp" },
".asmx": { "image": "asp" },
".aspx": { "image": "asp" },
".axd": { "image": "asp" },
"/\\.(l?a|[ls]?o|out|s|a51|asm|axf|elf|prx|puff|z80)$/i": { "image": "assembly" },
".agc": { "image": "assembly" },
".ko": { "image": "assembly" },
".lst": { "image": "assembly" },
"/\\.((c([+px]{2}?)?-?)?objdump|bsdiff|bin|dat|pak|pdb)$/i": { "image": "assembly" },
".d-objdump": { "image": "assembly" },
"/\\.gcode|\\.gco/i": { "image": "assembly" },
"/\\.rpy[bc]$/i": { "image": "assembly" },
"/\\.py[co]$/i": { "image": "assembly" },
".swp": { "image": "assembly" },
".DS_Store": { "image": "assembly" },
".au3": { "image": "autoit" },
"babel.config.js": { "image": "babel" },
"/\\.babelrc/i": { "image": "babel" },
".bat": { "image": "bat" },
".batch": { "image": "bat" },
".cmd": { "image": "bat" },
"/\\.(exe|com|msi)$/i": { "image": "bat" },
".reg": { "image": "bat" },
"/^(\\.bowerrc|bower\\.json|Bowerfile)$/i": { "image": "bower" },
"/\\.bf?$/i": { "image": "brainfuck" },
"/\\.c$/i": { "image": "c" },
"/(cargo.toml|cargo.lock)/i": { "image": "cargo" },
".cfc": { "image": "cfml" },
".cfm": { "image": "cfml" },
"circle.yml": { "image": "circleci" },
".clj": { "image": "clojure" },
".cl2": { "image": "clojure" },
".cljc": { "image": "clojure" },
".cljx": { "image": "clojure" },
".hic": { "image": "clojure" },
"/\\.cljs(cm)?$/i": { "image": "clojure" },
".cmake": { "image": "cmake" },
"/^CMakeLists\\.txt$/": { "image": "cmake" },
"/\\.codeclimate\\.(yml|json)/i": { "image": "codeclimate" },
".coffee": { "image": "coffeescript" },
".cjsx": { "image": "coffeescript" },
".coffee.ecr": { "image": "coffeescript" },
".coffee.erb": { "image": "coffeescript" },
".litcoffee": { "image": "coffeescript" },
".iced": { "image": "coffeescript" },
"/\\.c[+px]{2}$|\\.cc$/i": { "image": "cpp" },
"/\\.h[+px]{2}$/i": { "image": "cpp" },
"/\\.[it]pp$/i": { "image": "cpp" },
"/\\.(tcc|inl)$/i": { "image": "cpp" },
".cats": { "image": "cpp" },
".idc": { "image": "cpp" },
".w": { "image": "cpp" },
".nc": { "image": "cpp" },
".upc": { "image": "cpp" },
".xpm": { "image": "cpp" },
"/\\.e?cr$/i": { "image": "crystal" },
".cs": { "image": "csharp" },
".csx": { "image": "csharp" },
".cshtml": { "image": "cshtml" },
".css": { "image": "css" },
".css.map": { "image": "cssmap" },
".cu": { "image": "cuda" },
"/\\.di?$/i": { "image": "d" },
".dart": { "image": "dart" },
".dfm": { "image": "delphi" },
".dpr": { "image": "delphi" },
".dsc": { "image": "denizen" },
".dm": { "image": "dm" },
".dme": { "image": "dm" },
".dmm": { "image": "dm" },
"/^(Dockerfile|docker-compose)|\\.docker(file|ignore)$/i": { "image": "docker" },
"/^docker-sync\\.yml$/i": { "image": "docker" },
".editorconfig": { "image": "editorconfig" },
".ejs": { "image": "ejs" },
".ex": { "image": "elixir" },
"/\\.(exs|l?eex)$/i": { "image": "elixir" },
"/^mix\\.(exs?|lock)$/i": { "image": "elixir" },
".elm": { "image": "elm" },
".env": { "image": "env" },
".erl": { "image": "erlang" },
".beam": { "image": "erlang" },
".hrl": { "image": "erlang" },
".xrl": { "image": "erlang" },
".yrl": { "image": "erlang" },
".app.src": { "image": "erlang" },
"/^Emakefile$/": { "image": "erlang" },
"/^rebar(\\.config)?\\.lock$/i": { "image": "erlang" },
"/\\.(eslintrc|eslintignore)/i": { "image": "eslint" },
"/(\\.firebaserc|firebase\\.json)/i": { "image": "firebase" },
".flowconfig": { "image": "flowconfig" },
".fs": { "image": "fsharp" },
".fsi": { "image": "fsharp" },
".fsscript": { "image": "fsharp" },
".fsx": { "image": "fsharp" },
"/gatsby-(browser|node|ssr|config)\\.js/i": { "image": "gatsbyjs" },
"/^Gemfile(\\.lock)?$/i": { "image": "gemfile" },
"/^\\.git|^\\.keep$|\\.mailmap$/i": { "image": "git" },
".go": { "image": "go" },
".gd": { "image": "godot" },
".gradle": { "image": "gradle" },
"gradlew": { "image": "gradle" },
".gql": { "image": "graphql" },
".graphql": { "image": "graphql" },
".groovy": { "image": "groovy" },
".gvy": { "image": "groovy" },
".gy": { "image": "groovy" },
".gsh": { "image": "groovy" },
"/gruntfile\\.(js|coffee)/i": { "image": "gruntfile" },
"gulpfile.js": { "image": "gulp" },
"/\\.(hbs|handlebars|(mu)?stache)$/i": { "image": "handlebars" },
".prg": { "image": "harbour" },
".hbp": { "image": "harbour" },
".hbc": { "image": "harbour" },
".rc": { "image": "harbour" },
".fmg": { "image": "harbour" },
".hs": { "image": "haskell" },
".hsc": { "image": "haskell" },
".c2hs": { "image": "haskell" },
".lhs": { "image": "haskell" },
".hx": { "image": "haxe" },
".hxml": { "image": "haxe" },
"/^procfile/i": { "image": "heroku" },
"heroku.yml": { "image": "heroku" },
".hjson": { "image": "hjson" },
"/\\.x?html?$/i": { "image": "html" },
".http": { "image": "http" },
".rest": { "image": "http" },
".jar": { "image": "jar" },
".java": { "image": "java" },
"jest.config.js": { "image": "jest" },
".jinja": { "image": "jinja" },
".js": { "image": "javascript" },
".es6": { "image": "javascript" },
".es": { "image": "javascript" },
".mjs": { "image": "javascript" },
".js.map": { "image": "jsmap" },
".json": { "image": "json" },
".jsonc": { "image": "json" },
".jsx": { "image": "jsx" },
"/\\.(jil|jl)/i": { "image": "julia" },
".ipynb": { "image": "jupyter" },
".kt": { "image": "kotlin" },
".ktm": { "image": "kotlin" },
".kts": { "image": "kotlin" },
".less": { "image": "less" },
".lsp": { "image": "lisp" },
".lisp": { "image": "lisp" },
".l": { "image": "lisp" },
".nl": { "image": "lisp" },
".ny": { "image": "lisp" },
".podsl": { "image": "lisp" },
".sexp": { "image": "lisp" },
".ls": { "image": "livescript" },
".log": { "image": "log" },
".lua": { "image": "lua" },
".pd_lua": { "image": "lua" },
".rbxs": { "image": "lua" },
".wlua": { "image": "lua" },
"/^Makefile/": { "image": "makefile" },
"/^mk\\.config$/": { "image": "makefile" },
"/\\.(mk|mak|make)$/i": { "image": "makefile" },
"/^BSDmakefile$/i": { "image": "makefile" },
"/^GNUmakefile$/i": { "image": "makefile" },
"/^makefile\\.sco$/i": { "image": "makefile" },
"/^Kbuild$/": { "image": "makefile" },
"/^makefile$/": { "image": "makefile" },
"/^mkfile$/i": { "image": "makefile" },
"/^\\.?qmake$/i": { "image": "makefile" },
"/\\.(h|geo|topo)$/i": { "image": "manifest" },
".cson": { "image": "manifest" },
".json5": { "image": "manifest" },
".ndjson": { "image": "manifest" },
".fea": { "image": "manifest" },
".json.eex": { "image": "manifest" },
".proto": { "image": "manifest" },
".pytb": { "image": "manifest" },
".pydeps": { "image": "manifest" },
"/\\.pot?$/i": { "image": "manifest" },
".ejson": { "image": "manifest" },
".edn": { "image": "manifest" },
".eam.fs": { "image": "manifest" },
".qml": { "image": "manifest" },
".qbs": { "image": "manifest" },
".ston": { "image": "manifest" },
".ttl": { "image": "manifest" },
".rviz": { "image": "manifest" },
".syntax": { "image": "manifest" },
".webmanifest": { "image": "manifest" },
"/^pkginfo$/": { "image": "manifest" },
"/^mime\\.types$/i": { "image": "manifest" },
"/^METADATA\\.pb$/": { "image": "manifest" },
"/[\\/\\\\](?:magic[\\/\\\\]Magdir|file[\\/\\\\]magic)[\\/\\\\][-.\\w]+$/i": { "image": "manifest" },
"/(\\\\|\\/)dev[-\\w]+\\1(?:[^\\\\\\/]+\\1)*(?!DESC|NOTES)(?:[A-Z][-A-Z]*)(?:\\.in)?$/": { "image": "manifest" },
"lib/icons/.icondb.js": { "image": "manifest" },
"/\\.git[\\/\\\\](.*[\\/\\\\])?(HEAD|ORIG_HEAD|packed-refs|logs[\\/\\\\](.+[\\/\\\\])?[^\\/\\\\]+)$/": {
"image": "manifest"
},
"/\\.(md|mdown|markdown|mkd|mkdown|mdwn|mkdn|rmd|ron|pmd)$/i": { "image": "markdown" },
".mdx": { "image": "markdownx" },
".marko": { "image": "marko" },
".nim": { "image": "nim" },
".nims": { "image": "nim" },
".nimble": { "image": "nim" },
".nix": { "image": "nix" },
"nodemon.json": { "image": "nodemon" },
".npmrc": { "image": "npm" },
"/\\.mm?$/i": { "image": "objective-c" },
".pch": { "image": "objective-c" },
".x": { "image": "objective-c" },
".ml": { "image": "ocaml" },
".mli": { "image": "ocaml" },
".eliom": { "image": "ocaml" },
".eliomi": { "image": "ocaml" },
".ml4": { "image": "ocaml" },
".mll": { "image": "ocaml" },
".mly": { "image": "ocaml" },
"/\\.pas(cal)?$/i": { "image": "pascal" },
".lpr": { "image": "pascal" },
".p": { "image": "pawn" },
".inc": { "image": "pawn" },
".sma": { "image": "pawn" },
".pwn": { "image": "pawn" },
".sp": { "image": "pawn" },
"/\\.p(er)?l$/i": { "image": "perl" },
".al": { "image": "perl" },
".ph": { "image": "perl" },
".plx": { "image": "perl" },
".pm": { "image": "perl" },
"/\\.(psgi|xs)$/i": { "image": "perl" },
".pl6": { "image": "perl" },
"/\\.[tp]6$|\\.6pl$/i": { "image": "perl" },
"/\\.(pm6|p6m)$/i": { "image": "perl" },
".6pm": { "image": "perl" },
".nqp": { "image": "perl" },
".p6l": { "image": "perl" },
".pod6": { "image": "perl" },
"/^Rexfile$/": { "image": "perl" },
"/\\.php([st\\d]|_cs)?$/i": { "image": "php" },
"/^Phakefile/": { "image": "php" },
".pony": { "image": "ponylang" },
".pcss": { "image": "postcss" },
".ps1": { "image": "powershell" },
".psd1": { "image": "powershell" },
".psm1": { "image": "powershell" },
".ps1xml": { "image": "powershell" },
".prettierignore": { "image": "prettier" },
"/\\.prettier((rc)|(\\.(toml|yml|yaml|json|js))?$){2}/i": { "image": "prettier" },
"prettier.config.js": { "image": "prettier" },
"prisma.yml": { "image": "prisma" },
".pde": { "image": "processing" },
".jade": { "image": "pug" },
".pug": { "image": "pug" },
".purs": { "image": "purescript" },
".py": { "image": "python" },
".ipy": { "image": "python" },
".isolate": { "image": "python" },
".pep": { "image": "python" },
".gyp": { "image": "python" },
".gypi": { "image": "python" },
".pyde": { "image": "python" },
".pyp": { "image": "python" },
".pyt": { "image": "python" },
".py3": { "image": "python" },
".pyi": { "image": "python" },
".pyw": { "image": "python" },
".tac": { "image": "python" },
".wsgi": { "image": "python" },
".xpy": { "image": "python" },
".rpy": { "image": "python" },
"/\\.?(pypirc|pythonrc|python-venv)$/i": { "image": "python" },
"/^(SConstruct|SConscript)$/": { "image": "python" },
"/^(Snakefile|WATCHLISTS)$/": { "image": "python" },
"/^wscript$/": { "image": "python" },
"/\\.(r|Rprofile|rsx|rd)$/i": { "image": "r" },
".re": { "image": "reasonml" },
"/\\.(rb|ru|ruby|erb|gemspec|god|mspec|pluginspec|podspec|rabl|rake|opal)$/i": { "image": "ruby" },
"/^\\.?(irbrc|gemrc|pryrc|ruby-(gemset|version))$/i": { "image": "ruby" },
"/^(Appraisals|(Rake|[bB]uild|Cap|Danger|Deliver|Fast|Guard|Jar|Maven|Pod|Puppet|Snap)file(\\.lock)?)$/": {
"image": "ruby"
},
"/\\.(jbuilder|rbuild|rb[wx]|builder)$/i": { "image": "ruby" },
"/^rails$/": { "image": "ruby" },
".watchr": { "image": "ruby" },
".rs": { "image": "rust" },
"/\\.(sc|scala)$/i": { "image": "scala" },
".scss": { "image": "scss" },
".sass": { "image": "scss" },
"/\\.(sh|rc|bats|bash|tool|install|command)$/i": { "image": "shell" },
"/^(\\.?bash(rc|[-_]?(profile|login|logout|history|prompt))|_osc|config|install-sh|PKGBUILD)$/i": {
"image": "shell"
},
"/\\.(ksh|mksh|pdksh)$/i": { "image": "shell" },
".sh-session": { "image": "shell" },
"/\\.zsh(-theme|_history)?$|^\\.?(antigen|zpreztorc|zlogin|zlogout|zprofile|zshenv|zshrc)$/i": { "image": "shell" },
"/\\.fish$|^\\.fishrc$/i": { "image": "shell" },
"/^\\.?(login|profile)$/": { "image": "shell" },
".inputrc": { "image": "shell" },
".tmux": { "image": "shell" },
"/^(configure|config\\.(guess|rpath|status|sub)|depcomp|libtool|compile)$/": { "image": "shell" },
"/^\\/(private\\/)?etc\\/([^\\/]+\\/)*(profile$|nanorc$|rc\\.|csh\\.)/i": { "image": "shell" },
"/^\\.?cshrc$/i": { "image": "shell" },
".profile": { "image": "shell" },
".tcsh": { "image": "shell" },
".csh": { "image": "shell" },
".sqf": { "image": "sqf" },
"/\\.(my)?sql$/i": { "image": "sql" },
".ddl": { "image": "sql" },
".udf": { "image": "sql" },
".hql": { "image": "sql" },
".viw": { "image": "sql" },
".prc": { "image": "sql" },
".cql": { "image": "sql" },
".db2": { "image": "sql" },
"/\\.(styl|stylus)$/i": { "image": "stylus" },
".svelte": { "image": "svelte" },
".svg": { "image": "svg" },
".swift": { "image": "swift" },
".tex": { "image": "tex" },
".ltx": { "image": "tex" },
".aux": { "image": "tex" },
".sty": { "image": "tex" },
".dtx": { "image": "tex" },
".cls": { "image": "tex" },
".ins": { "image": "tex" },
".lbx": { "image": "tex" },
".mkiv": { "image": "tex" },
".mkvi": { "image": "tex" },
".mkii": { "image": "tex" },
".texi": { "image": "tex" },
"/^hyphen(ex)?\\.(cs|den|det|fr|sv|us)$/": { "image": "tex" },
"/\\.te?xt$/i": { "image": "text" },
".rtf": { "image": "text" },
"/\\.i?nfo$/i": { "image": "text" },
".msg": { "image": "text" },
"/\\.(utxt|utf8)$/i": { "image": "text" },
".toml": { "image": "toml" },
".travis.yml": { "image": "travis" },
".ts": { "image": "typescript" },
".tsx": { "image": "tsx" },
".twig": { "image": "twig" },
"/.*\\.d\\.ts/i": { "image": "typescript-def" },
".v": { "image": "v" },
".vh": { "image": "v" },
".vala": { "image": "vala" },
".vapi": { "image": "vala" },
".vb": { "image": "vb" },
".vbs": { "image": "vb" },
".vbhtml": { "image": "vb" },
".vbproj": { "image": "vb" },
".vba": { "image": "vba" },
".vcxproj": { "image": "vcxproj" },
".vscodeignore": { "image": "vscodeignore" },
".vue": { "image": "vue" },
".wat": { "image": "wasm" },
".wast": { "image": "wasm" },
".wasm": { "image": "wasm" },
"/webpack(\\.dev|\\.development|\\.prod|\\.production)?\\.config(\\.babel)?\\.(js|jsx|coffee|ts|json|json5|yaml|yml)/i": {
"image": "webpack"
},
".xml": { "image": "xml" },
"/\\.ya?ml$/i": { "image": "yaml" },
"/^yarn(\\.lock)?$/i": { "image": "yarn" },
".yarnrc": { "image": "yarn" },
".zig": { "image": "zig" }
}
}

View file

@ -1,112 +1,118 @@
import { commands, ExtensionContext, StatusBarAlignment, StatusBarItem, window, workspace, extensions } from 'vscode';
import RPCClient from './client/RPCClient';
import Logger from './structures/Logger';
import { GitExtension } from './git';
const { register } = require('discord-rpc'); // eslint-disable-line
const { Client } = require('discord-rpc'); // eslint-disable-line
import {
commands,
ExtensionContext,
StatusBarAlignment,
StatusBarItem,
window,
workspace,
extensions,
debug,
} from 'vscode';
import { activity } from './activity';
let loginTimeout: NodeJS.Timer | undefined;
import { CLIENT_ID, CONFIG_KEYS } from './constants';
import { GitExtension } from './git';
import { log, LogLevel } from './logger';
import { getConfig } from './util';
const statusBarIcon: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
statusBarIcon.text = '$(pulse) Connecting to Discord...';
const clientId = '383226320970055681';
const config = workspace.getConfiguration('discord');
register(clientId);
const rpc = new RPCClient(clientId, statusBarIcon);
const rpc = new Client({ transport: 'ipc' });
const config = getConfig();
export async function activate(context: ExtensionContext) {
Logger.log('Discord Presence activated!');
async function sendActivity() {
rpc.setActivity(await activity());
}
async function login(context: ExtensionContext) {
rpc.once('ready', () => {
log(LogLevel.Info, 'Successfully connected to Discord');
statusBarIcon.text = '$(globe) Connected to Discord';
statusBarIcon.tooltip = 'Connected to Discord';
void sendActivity();
const onChangeActiveTextEditor = window.onDidChangeActiveTextEditor(() => sendActivity());
const onChangeTextDocument = workspace.onDidChangeTextDocument(() => sendActivity());
const onStartDebugSession = debug.onDidStartDebugSession(() => sendActivity());
const onTerminateDebugSession = debug.onDidTerminateDebugSession(() => sendActivity());
context.subscriptions.push(
onChangeActiveTextEditor,
onChangeTextDocument,
onStartDebugSession,
onTerminateDebugSession,
);
});
try {
await rpc.login({ clientId: CLIENT_ID });
} catch (error) {
log(LogLevel.Error, `Encountered following error while trying to login:\n${error as string}`);
rpc.dispose();
if (!config[CONFIG_KEYS.SuppressNotifications]) {
if (error?.message?.includes('ENOENT')) void window.showErrorMessage('No Discord client detected');
else void window.showErrorMessage(`Couldn't connect to Discord via RPC: ${error as string}`);
}
rpc.statusBarIcon.text = '$(pulse) Reconnect to Discord';
rpc.statusBarIcon.command = 'discord.reconnect';
}
}
export function activate(context: ExtensionContext) {
log(LogLevel.Info, 'Discord Presence activated');
let isWorkspaceExcluded = false;
const excludePatterns = config.get<string[]>('workspaceExcludePatterns');
if (excludePatterns?.length) {
for (const pattern of excludePatterns) {
const regex = new RegExp(pattern);
const folders = workspace.workspaceFolders;
if (!folders) break;
if (folders.some((folder) => regex.test(folder.uri.fsPath))) {
isWorkspaceExcluded = true;
break;
}
for (const pattern of config[CONFIG_KEYS.WorkspaceExcludePatterns]) {
const regex = new RegExp(pattern);
const folders = workspace.workspaceFolders;
if (!folders) break;
if (folders.some((folder) => regex.test(folder.uri.fsPath))) {
isWorkspaceExcluded = true;
break;
}
}
const enabler = commands.registerCommand('discord.enable', () => {
rpc.dispose();
rpc.destroy();
void config.update('enabled', true);
rpc.config = workspace.getConfiguration('discord');
rpc.statusBarIcon.text = '$(pulse) Connecting to Discord...';
rpc.statusBarIcon.show();
void rpc.login();
void window.showInformationMessage('Enabled Discord Rich Presence for this workspace.');
statusBarIcon.text = '$(pulse) Connecting to Discord...';
statusBarIcon.show();
void login(context);
void window.showInformationMessage('Enabled Discord Presence for this workspace');
});
const disabler = commands.registerCommand('discord.disable', () => {
void config.update('enabled', false);
rpc.config = workspace.getConfiguration('discord');
rpc.dispose();
rpc.destroy();
rpc.statusBarIcon.hide();
void window.showInformationMessage('Disabled Discord Rich Presence for this workspace.');
void window.showInformationMessage('Disabled Discord Presence for this workspace');
});
const reconnecter = commands.registerCommand('discord.reconnect', () => {
if (loginTimeout) clearTimeout(loginTimeout);
rpc.dispose();
loginTimeout = setTimeout(() => {
void rpc.login();
if (!config.get('silent')) void window.showInformationMessage('Reconnecting to Discord RPC...');
rpc.statusBarIcon.text = '$(pulse) Reconnecting to Discord...';
rpc.statusBarIcon.command = 'discord.reconnect';
}, 1000);
deactivate();
void activate(context);
});
const disconnect = commands.registerCommand('discord.disconnect', () => {
rpc.dispose();
rpc.destroy();
rpc.statusBarIcon.text = '$(pulse) Reconnect to Discord';
rpc.statusBarIcon.command = 'discord.reconnect';
});
const allowSpectate = commands.registerCommand('discord.allowSpectate', () => rpc.allowSpectate());
const disableSpectate = commands.registerCommand('discord.disableSpectate', () => rpc.disableSpectate());
const allowJoinRequests = commands.registerCommand('discord.allowJoinRequests', () => rpc.allowJoinRequests());
const disableJoinRequests = commands.registerCommand('discord.disableJoinRequests', () => rpc.disableJoinRequests());
context.subscriptions.push(
enabler,
disabler,
reconnecter,
disconnect,
allowSpectate,
disableSpectate,
allowJoinRequests,
disableJoinRequests,
);
context.subscriptions.push(enabler, disabler, reconnecter, disconnect);
if (!isWorkspaceExcluded && config.get<boolean>('enabled')) {
if (!isWorkspaceExcluded && config[CONFIG_KEYS.Enabled]) {
statusBarIcon.show();
try {
await rpc.login();
} catch (error) {
Logger.log(`Encountered following error after trying to login:\n${error as string}`);
rpc.dispose();
if (!config.get('silent')) {
if (error?.message?.includes('ENOENT')) void window.showErrorMessage('No Discord Client detected!');
else void window.showErrorMessage(`Couldn't connect to Discord via RPC: ${error as string}`);
}
rpc.statusBarIcon.text = '$(pulse) Reconnect to Discord';
rpc.statusBarIcon.command = 'discord.reconnect';
}
void login(context);
}
const gitExtension = extensions.getExtension<GitExtension>('vscode.git');
if (gitExtension) {
if (gitExtension.isActive) {
rpc.git = gitExtension.exports.getAPI(1);
} else {
const extension = await gitExtension.activate();
rpc.git = extension.getAPI(1);
}
}
void gitExtension?.activate();
}
export function deactivate() {
rpc.dispose();
rpc.destroy();
}
process.on('unhandledRejection', (err) => Logger.log(err as string));

95
src/git.d.ts vendored
View file

@ -1,10 +1,10 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// eslint-disable-next-line
import { Uri, SourceControlInputBox, Event, CancellationToken } from 'vscode';
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
export { ProviderResult } from 'vscode';
export interface Git {
readonly path: string;
@ -14,6 +14,11 @@ export interface InputBox {
value: string;
}
export const enum ForcePushMode {
Force,
ForceWithLease,
}
export const enum RefType {
Head,
RemoteHead,
@ -42,7 +47,10 @@ export interface Commit {
readonly hash: string;
readonly message: string;
readonly parents: string[];
readonly authorEmail?: string | undefined;
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
}
export interface Submodule {
@ -117,6 +125,24 @@ export interface RepositoryUIState {
export interface LogOptions {
/** Max number of log entries to retrieve. If not specified, the default is 32. */
readonly maxEntries?: number;
readonly path?: string;
}
export interface CommitOptions {
all?: boolean | 'tracked';
amend?: boolean;
signoff?: boolean;
signCommit?: boolean;
empty?: boolean;
noVerify?: boolean;
requireUserConfig?: boolean;
}
export interface BranchQuery {
readonly remote?: boolean;
readonly pattern?: string;
readonly count?: number;
readonly contains?: string;
}
export interface Repository {
@ -157,6 +183,7 @@ export interface Repository {
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
deleteBranch(name: string, force?: boolean): Promise<void>;
getBranch(name: string): Promise<Branch>;
getBranches(query: BranchQuery): Promise<Ref[]>;
setBranchUpstream(name: string, upstream: string): Promise<void>;
getMergeBase(ref1: string, ref2: string): Promise<string>;
@ -166,24 +193,75 @@ export interface Repository {
addRemote(name: string, url: string): Promise<void>;
removeRemote(name: string): Promise<void>;
renameRemote(name: string, newName: string): Promise<void>;
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise<void>;
blame(path: string): Promise<string>;
log(options?: LogOptions): Promise<Commit[]>;
commit(message: string, opts?: CommitOptions): Promise<void>;
}
export interface RemoteSource {
readonly name: string;
readonly description?: string;
readonly url: string | string[];
}
export interface RemoteSourceProvider {
readonly name: string;
readonly icon?: string; // codicon name
readonly supportsQuery?: boolean;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
getBranches?(url: string): ProviderResult<string[]>;
publishRepository?(repository: Repository): Promise<void>;
}
export interface Credentials {
readonly username: string;
readonly password: string;
}
export interface CredentialsProvider {
getCredentials(host: Uri): ProviderResult<Credentials>;
}
export interface PushErrorHandler {
handlePushError(
repository: Repository,
remote: Remote,
refspec: string,
error: Error & { gitErrorCode: GitErrorCodes },
): Promise<boolean>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface PublishEvent {
repository: Repository;
branch?: string;
}
export interface API {
readonly state: APIState;
readonly onDidChangeState: Event<APIState>;
readonly onDidPublish: Event<PublishEvent>;
readonly git: Git;
readonly repositories: Repository[];
readonly onDidOpenRepository: Event<Repository>;
readonly onDidCloseRepository: Event<Repository>;
toGitUri(uri: Uri, ref: string): Uri;
getRepository(uri: Uri): Repository | null;
init(root: Uri): Promise<Repository | null>;
openRepository(root: Uri): Promise<Repository | null>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
}
export interface GitExtension {
@ -220,6 +298,7 @@ export const enum GitErrorCodes {
CantOpenResource = 'CantOpenResource',
GitNotFound = 'GitNotFound',
CantCreatePipe = 'CantCreatePipe',
PermissionDenied = 'PermissionDenied',
CantAccessRemote = 'CantAccessRemote',
RepositoryNotFound = 'RepositoryNotFound',
RepositoryIsLocked = 'RepositoryIsLocked',

33
src/logger.ts Normal file
View file

@ -0,0 +1,33 @@
import { window } from 'vscode';
import dayjs from 'dayjs';
const outputChannel = window.createOutputChannel('Discord Presence');
export const enum LogLevel {
Trace = 'TRACE',
Debug = 'DEBUG',
Info = 'INFO',
Warn = 'WARN',
Error = 'ERROR',
}
function send(level: string, message: string) {
outputChannel.appendLine(`[${dayjs().format('DD/MM/YYYY HH:mm:ss')} - ${level}] ${message}`);
}
export function log(level: LogLevel, message: string | Error) {
if (typeof message === 'string') {
send(level, message);
} else if (message instanceof Error) {
send(level, message.message);
if (message.stack) {
send(level, message.stack);
}
} else if (typeof message === 'object') {
try {
const json = JSON.stringify(message, null, 2);
send(level, json);
} catch {}
}
}

View file

@ -1,376 +0,0 @@
import { basename, parse, sep } from 'path';
import { debug, Disposable, env, window, workspace } from 'vscode';
import * as vsls from 'vsls';
import RPCClient from '../client/RPCClient';
import lang from '../data/languages.json';
const knownExtensions: { [key: string]: { image: string } } = lang.knownExtensions;
const knownLanguages: string[] = lang.knownLanguages;
const empty = '\u200b\u200b';
const sizes = [' bytes', 'kb', 'mb', 'gb', 'tb'];
export interface State {
details?: string;
state?: string;
startTimestamp?: number | null;
largeImageKey?: string;
largeImageText?: string;
smallImageKey?: string;
smallImageText?: string;
partyId?: string;
partySize?: number;
partyMax?: number;
matchSecret?: string;
joinSecret?: string;
spectateSecret?: string;
instance?: boolean;
}
interface FileDetail {
size?: string;
totalLines?: string;
currentLine?: string;
currentColumn?: string;
gitbranch?: string;
gitreponame?: string;
}
export default class Activity implements Disposable {
private _state: State | null = null;
private lastKnownFile = '';
public constructor(private readonly client: RPCClient) {}
public get state() {
return this._state;
}
public async generate(workspaceElapsedTime = false) {
let largeImageKey: any = 'vscode-big';
if (window.activeTextEditor) {
if (window.activeTextEditor.document.languageId === 'Log') return this._state;
if (this._state && window.activeTextEditor.document.fileName === this.lastKnownFile) {
return (this._state = {
...this._state,
details: await this._generateDetails(
'detailsDebugging',
'detailsEditing',
'detailsIdle',
this._state.largeImageKey,
),
smallImageKey: debug.activeDebugSession
? 'debug'
: env.appName.includes('Insiders')
? 'vscode-insiders'
: 'vscode',
state: await this._generateDetails(
'lowerDetailsDebugging',
'lowerDetailsEditing',
'lowerDetailsIdle',
this._state.largeImageKey,
),
});
}
this.lastKnownFile = window.activeTextEditor.document.fileName;
const filename = basename(window.activeTextEditor.document.fileName);
largeImageKey =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
knownExtensions[
Object.keys(knownExtensions).find((key) => {
if (filename.endsWith(key)) return true;
const match = /^\/(.*)\/([mgiy]+)$/.exec(key);
if (!match) return false;
const regex = new RegExp(match[1], match[2]);
return regex.test(filename);
})!
] ??
(knownLanguages.includes(window.activeTextEditor.document.languageId)
? window.activeTextEditor.document.languageId
: null);
}
let previousTimestamp = null;
if (this._state?.startTimestamp) previousTimestamp = this._state.startTimestamp;
this._state = {
...this._state,
details: await this._generateDetails('detailsDebugging', 'detailsEditing', 'detailsIdle', largeImageKey),
startTimestamp:
window.activeTextEditor && previousTimestamp && workspaceElapsedTime
? previousTimestamp
: window.activeTextEditor
? new Date().getTime()
: null,
state: await this._generateDetails(
'lowerDetailsDebugging',
'lowerDetailsEditing',
'lowerDetailsIdle',
largeImageKey,
),
largeImageKey: largeImageKey ? largeImageKey.image || largeImageKey : 'txt',
largeImageText: window.activeTextEditor
? this.client.config
.get<string>('largeImage')!
.replace('{lang}', largeImageKey ? largeImageKey.image || largeImageKey : 'txt')
.replace(
'{Lang}',
largeImageKey
? (largeImageKey.image || largeImageKey).toLowerCase().replace(/^\w/, (c: string) => c.toUpperCase())
: 'Txt',
)
.replace('{LANG}', largeImageKey ? (largeImageKey.image || largeImageKey).toUpperCase() : 'TXT') ||
window.activeTextEditor.document.languageId.padEnd(2, '\u200b')
: this.client.config.get<string>('largeImageIdle'),
smallImageKey: debug.activeDebugSession
? 'debug'
: env.appName.includes('Insiders')
? 'vscode-insiders'
: 'vscode',
smallImageText: this.client.config.get<string>('smallImage')!.replace('{appname}', env.appName),
};
return this._state;
}
public async allowSpectate() {
if (!this._state) return;
const liveshare = await vsls.getApi();
if (!liveshare) return;
const join = await liveshare.share({ suppressNotification: true, access: vsls.Access.ReadOnly });
this._state = {
...this._state,
spectateSecret: join ? Buffer.from(join.toString()).toString('base64') : undefined,
instance: true,
};
return this._state;
}
public async disableSpectate() {
if (!this._state) return;
const liveshare = await vsls.getApi();
if (!liveshare) return;
await liveshare.end();
delete this._state.spectateSecret;
this._state.instance = false;
return this._state;
}
public async allowJoinRequests() {
if (!this._state) return;
const liveshare = await vsls.getApi();
if (!liveshare) return;
const join = await liveshare.share({ suppressNotification: true });
this._state = {
...this._state,
partyId: join ? join.query : undefined,
partySize: 1,
partyMax: 5,
joinSecret: join ? Buffer.from(join.toString()).toString('base64') : undefined,
instance: true,
};
return this._state;
}
public async disableJoinRequests() {
if (!this._state) return;
const liveshare = await vsls.getApi();
if (!liveshare) return;
await liveshare.end();
delete this._state.partyId;
delete this._state.partySize;
delete this._state.partyMax;
delete this._state.joinSecret;
this._state.instance = false;
return this._state;
}
public changePartyId(id?: string) {
if (!this._state) return;
if (!id) {
delete this._state.partyId;
delete this._state.partySize;
delete this._state.partyMax;
this._state.instance = false;
return this._state;
}
this._state = {
...this._state,
partyId: id,
partySize: this._state.partySize ? this._state.partySize + 1 : 1,
partyMax: 5,
instance: true,
};
return this._state;
}
public increasePartySize(size?: number) {
if (!this._state) return;
if (this._state.partySize === 5) return;
this._state = {
...this._state,
partySize: this._state.partySize ? this._state.partySize + 1 : size,
};
return this._state;
}
public decreasePartySize(size?: number) {
if (!this._state) return;
if (this._state.partySize === 1) return;
this._state = {
...this._state,
partySize: this._state.partySize ? this._state.partySize - 1 : size,
};
return this._state;
}
public dispose() {
this._state = null;
this.lastKnownFile = '';
}
private async _generateDetails(debugging: string, editing: string, idling: string, largeImageKey: any) {
let raw = this.client.config.get<string>(idling)!.replace('{null}', empty);
let filename = null;
let dirname = null;
let checkState = false;
let workspaceName = null;
let workspaceFolder = null;
let fullDirname = null;
if (window.activeTextEditor) {
filename = basename(window.activeTextEditor.document.fileName);
const { dir } = parse(window.activeTextEditor.document.fileName);
const split = dir.split(sep);
dirname = split[split.length - 1];
checkState = Boolean(workspace.getWorkspaceFolder(window.activeTextEditor.document.uri));
workspaceName = workspace.name;
workspaceFolder = checkState ? workspace.getWorkspaceFolder(window.activeTextEditor.document.uri) : null;
if (workspaceFolder) {
const { name } = workspaceFolder;
const relativePath = workspace.asRelativePath(window.activeTextEditor.document.fileName).split(sep);
relativePath.splice(-1, 1);
fullDirname = `${name}${sep}${relativePath.join(sep)}`;
}
if (debug.activeDebugSession) {
raw = this.client.config.get<string>(debugging)!;
} else {
raw = this.client.config.get<string>(editing)!;
}
const { totalLines, size, currentLine, currentColumn, gitbranch, gitreponame } = await this._generateFileDetails(
raw,
);
raw = raw
.replace('{null}', empty)
.replace('{filename}', filename)
.replace('{dirname}', dirname)
.replace('{fulldirname}', fullDirname!)
.replace(
'{workspace}',
workspaceName
? workspaceName
: checkState && workspaceFolder
? workspaceFolder.name
: this.client.config.get<string>('lowerDetailsNotFound')!.replace('{null}', empty),
)
.replace(
'{workspaceFolder}',
checkState && workspaceFolder
? workspaceFolder.name
: this.client.config.get<string>('lowerDetailsNotFound')!.replace('{null}', empty),
)
.replace(
'{workspaceAndFolder}',
checkState && workspaceName && workspaceFolder
? `${workspaceName} - ${workspaceFolder.name}`
: this.client.config.get<string>('lowerDetailsNotFound')!.replace('{null}', empty),
)
.replace('{lang}', largeImageKey ? largeImageKey.image || largeImageKey : 'txt')
.replace(
'{Lang}',
largeImageKey
? (largeImageKey.image || largeImageKey).toLowerCase().replace(/^\w/, (c: string) => c.toUpperCase())
: 'Txt',
)
.replace('{LANG}', largeImageKey ? (largeImageKey.image || largeImageKey).toUpperCase() : 'TXT');
if (totalLines) raw = raw.replace('{totallines}', totalLines);
if (size) raw = raw.replace('{filesize}', size);
if (currentLine) raw = raw.replace('{currentline}', currentLine);
if (currentColumn) raw = raw.replace('{currentcolumn}', currentColumn);
if (gitbranch) raw = raw.replace('{gitbranch}', gitbranch);
if (gitreponame) raw = raw.replace('{gitreponame}', gitreponame);
}
return raw;
}
private async _generateFileDetails(str?: string) {
const fileDetail: FileDetail = {};
if (!str) return fileDetail;
if (window.activeTextEditor) {
if (str.includes('{totallines}')) {
fileDetail.totalLines = window.activeTextEditor.document.lineCount.toLocaleString();
}
if (str.includes('{currentline}')) {
fileDetail.currentLine = (window.activeTextEditor.selection.active.line + 1).toLocaleString();
}
if (str.includes('{currentcolumn}')) {
fileDetail.currentColumn = (window.activeTextEditor.selection.active.character + 1).toLocaleString();
}
if (str.includes('{filesize}')) {
let currentDivision = 0;
let { size } = await workspace.fs.stat(window.activeTextEditor.document.uri);
const originalSize = size;
if (originalSize > 1000) {
size /= 1000;
currentDivision++;
while (size > 1000) {
currentDivision++;
size /= 1000;
}
}
fileDetail.size = `${originalSize > 1000 ? size.toFixed(2) : size}${sizes[currentDivision]}`;
}
if (str.includes('{gitbranch}')) {
if (this.client.git?.repositories.length) {
fileDetail.gitbranch = this.client.git.repositories.find((repo) => repo.ui.selected)!.state.HEAD!.name;
} else {
fileDetail.gitbranch = 'Unknown';
}
}
if (str.includes('{gitreponame}')) {
if (this.client.git?.repositories.length) {
fileDetail.gitreponame = this.client.git.repositories
.find((repo) => repo.ui.selected)!
.state.remotes[0].fetchUrl!.split('/')[1]
.replace('.git', '');
} else {
fileDetail.gitreponame = 'Unknown';
}
}
}
return fileDetail;
}
}

View file

@ -1,13 +0,0 @@
import { OutputChannel, window } from 'vscode';
// eslint-disable-next-line
export default class Logger {
private static output?: OutputChannel;
public static log(message: string) {
if (!this.output) {
this.output = window.createOutputChannel('Discord Presence');
}
this.output.appendLine(message);
}
}

56
src/util.ts Normal file
View file

@ -0,0 +1,56 @@
import { basename } from 'path';
import { TextDocument, workspace, WorkspaceConfiguration } from 'vscode';
import { KNOWN_EXTENSIONS, KNOWN_LANGUAGES } from './constants';
type WorkspaceExtensionConfigurationuration = WorkspaceConfiguration & {
enabled: boolean;
details_editing: string;
details_debugging: string;
details_idling: string;
lower_details_editing: string;
lower_details_debugging: string;
lower_details_idling: string;
lower_details_no_workspace_found: string;
large_image: string;
large_image_idling: string;
small_image: string;
suppress_notifications: boolean;
workspace_elapsed_time: boolean;
workspace_exclude_patterns: string[];
};
export function getConfig() {
return workspace.getConfiguration('discord') as WorkspaceExtensionConfigurationuration;
}
export const toLower = (str: string) => str.toLocaleLowerCase();
export const toUpper = (str: string) => str.toLocaleUpperCase();
export const toTitle = (str: string) => toLower(str).replace(/^\w/, (c) => toUpper(c));
export function resolveFileIcon(document: TextDocument) {
const filename = basename(document.fileName);
const findKnownExtension = Object.keys(KNOWN_EXTENSIONS).find((key) => {
if (filename.endsWith(key)) {
return true;
}
const match = /^\/(.*)\/([mgiy]+)$/.exec(key);
if (!match) {
return false;
}
const regex = new RegExp(match[1], match[2]);
return regex.test(filename);
});
const findKnownLanguage = KNOWN_LANGUAGES.find((key) => key.language === document.languageId);
const fileIcon = findKnownExtension
? KNOWN_EXTENSIONS[findKnownExtension]
: findKnownLanguage
? findKnownLanguage.image
: null;
return typeof fileIcon === 'string' ? fileIcon : fileIcon?.image ?? 'text';
}

View file

@ -1,7 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowJs": true,
"allowJs": true
},
"include": [
"**/*.ts",
@ -11,7 +11,7 @@
"**/*.test.ts",
"**/*.test.js",
"**/*.spec.ts",
"**/*.spec.js",
"**/*.spec.js"
],
"exclude": []
}

View file

@ -6,11 +6,9 @@
"removeComments": false,
"alwaysStrict": true,
"pretty": true,
"target": "es2017",
"target": "ES2019",
"module": "commonjs",
"lib": [
"ESNext"
],
"lib": ["ESNext"],
"outDir": "dist",
"sourceMap": true,
"inlineSources": true,

View file

@ -1,7 +1,11 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-require-imports */
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
/** @type {import('webpack').Configuration} */
module.exports = {
target: 'node',
entry: './src/extension.ts',