refactor: big rewrite
This commit is contained in:
parent
3b3f41af23
commit
aa5e0c97da
21 changed files with 4232 additions and 1403 deletions
23
.github/workflows/lint.yml
vendored
23
.github/workflows/lint.yml
vendored
|
@ -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
23
.github/workflows/test.yml
vendored
Normal 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
|
23
.github/workflows/tsc.yml
vendored
23
.github/workflows/tsc.yml
vendored
|
@ -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
8
.prettierrc.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"printWidth": 120,
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "as-needed",
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf"
|
||||
}
|
2
LICENSE
2
LICENSE
|
@ -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
|
||||
|
|
20
README.md
20
README.md
|
@ -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
3291
package-lock.json
generated
File diff suppressed because it is too large
Load diff
111
package.json
111
package.json
|
@ -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
193
src/activity.ts
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -1,145 +1,68 @@
|
|||
{
|
||||
"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"
|
||||
"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" }
|
||||
],
|
||||
"knownExtensions": {
|
||||
"KNOWN_EXTENSIONS": {
|
||||
".ahk": { "image": "ahk" },
|
||||
".ahkl": { "image": "ahk" },
|
||||
"androidmanifest.xml": { "image": "android" },
|
||||
|
@ -193,12 +116,12 @@
|
|||
".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" },
|
||||
".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" },
|
||||
|
@ -285,10 +208,10 @@
|
|||
".java": { "image": "java" },
|
||||
"jest.config.js": { "image": "jest" },
|
||||
".jinja": { "image": "jinja" },
|
||||
".js": { "image": "js" },
|
||||
".es6": { "image": "js" },
|
||||
".es": { "image": "js" },
|
||||
".mjs": { "image": "js" },
|
||||
".js": { "image": "javascript" },
|
||||
".es6": { "image": "javascript" },
|
||||
".es": { "image": "javascript" },
|
||||
".mjs": { "image": "javascript" },
|
||||
".js.map": { "image": "jsmap" },
|
||||
".json": { "image": "json" },
|
||||
".jsonc": { "image": "json" },
|
||||
|
@ -360,9 +283,9 @@
|
|||
".nix": { "image": "nix" },
|
||||
"nodemon.json": { "image": "nodemon" },
|
||||
".npmrc": { "image": "npm" },
|
||||
"/\\.mm?$/i": { "image": "objc" },
|
||||
".pch": { "image": "objc" },
|
||||
".x": { "image": "objc" },
|
||||
"/\\.mm?$/i": { "image": "objective-c" },
|
||||
".pch": { "image": "objective-c" },
|
||||
".x": { "image": "objective-c" },
|
||||
".ml": { "image": "ocaml" },
|
||||
".mli": { "image": "ocaml" },
|
||||
".eliom": { "image": "ocaml" },
|
||||
|
@ -491,7 +414,7 @@
|
|||
"/\\.(utxt|utf8)$/i": { "image": "text" },
|
||||
".toml": { "image": "toml" },
|
||||
".travis.yml": { "image": "travis" },
|
||||
".ts": { "image": "ts" },
|
||||
".ts": { "image": "typescript" },
|
||||
".tsx": { "image": "tsx" },
|
||||
".twig": { "image": "twig" },
|
||||
"/.*\\.d\\.ts/i": { "image": "typescript-def" },
|
||||
|
@ -510,7 +433,9 @@
|
|||
".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" },
|
||||
"/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" },
|
||||
|
|
150
src/extension.ts
150
src/extension.ts
|
@ -1,26 +1,71 @@
|
|||
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) {
|
||||
for (const pattern of config[CONFIG_KEYS.WorkspaceExcludePatterns]) {
|
||||
const regex = new RegExp(pattern);
|
||||
const folders = workspace.workspaceFolders;
|
||||
if (!folders) break;
|
||||
|
@ -29,84 +74,45 @@ export async function activate(context: ExtensionContext) {
|
|||
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));
|
||||
|
|
91
src/git.d.ts
vendored
91
src/git.d.ts
vendored
|
@ -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.
|
||||
*/
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// 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
33
src/logger.ts
Normal 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 {}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
56
src/util.ts
Normal 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';
|
||||
}
|
|
@ -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": []
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in a new issue