refactor: more languages and error handling

This commit is contained in:
iCrawl 2022-01-30 18:43:29 +01:00
parent b4d08830f1
commit 5edd25d153
No known key found for this signature in database
GPG key ID: 1AB888B16355FBB2
14 changed files with 1297 additions and 253 deletions

View file

@ -3,8 +3,5 @@
"extends": "marine/prettier/node",
"parserOptions": {
"project": "./tsconfig.eslint.json"
},
"rules": {
"@typescript-eslint/naming-convention": 0
}
}

View file

@ -1,10 +1,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
- package-ecosystem: npm
directory: '/'
schedule:
interval: daily
time: "15:00"
timezone: Europe/Vienna
open-pull-requests-limit: 99

4
.vscode/launch.json vendored
View file

@ -6,10 +6,10 @@
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/dist/**/*.js" ]
"outFiles": ["${workspaceRoot}/dist/**/*.js"]
}
]
}

View file

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

1144
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,10 +18,9 @@
"license": "MIT",
"main": "./dist/extension",
"scripts": {
"prebuild": "npm run lint",
"build": "webpack --mode production",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix"
"build": "npm run lint && webpack --mode production",
"lint": "prettier --check . && eslint src --ext mjs,js,ts",
"format": "prettier --write . && eslint src --ext mjs,js,ts --fix"
},
"activationEvents": [
"*"
@ -198,13 +197,13 @@
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"clean-webpack-plugin": "^4.0.0",
"eslint": "^8.7.0",
"eslint": "^8.8.0",
"eslint-config-marine": "^9.3.2",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.1",
"terser-webpack-plugin": "^5.3.0",
"ts-loader": "^9.2.6",
"tsup": "^5.11.13",
"typescript": "^4.5.5",
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"

View file

@ -18,21 +18,142 @@ import { log, LogLevel } from './logger';
import { getConfig, getGit, 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;
buttons?: { label: string; url: string }[];
instance?: boolean;
details?: string | undefined;
state?: string | undefined;
startTimestamp?: number | null | undefined;
largeImageKey?: string | undefined;
largeImageText?: string | undefined;
smallImageKey?: string | undefined;
smallImageText?: string | undefined;
partyId?: string | undefined;
partySize?: number | undefined;
partyMax?: number | undefined;
matchSecret?: string | undefined;
joinSecret?: string | undefined;
spectateSecret?: string | undefined;
buttons?: { label: string; url: string }[] | undefined;
instance?: boolean | undefined;
}
async function fileDetails(_raw: string, document: TextDocument, selection: Selection) {
let raw = _raw.slice();
if (raw.includes(REPLACE_KEYS.TotalLines)) {
raw = raw.replace(REPLACE_KEYS.TotalLines, document.lineCount.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: number;
try {
({ size } = await workspace.fs.stat(document.uri));
} catch {
size = document.getText().length;
}
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]}`,
);
}
const git = await getGit();
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 ?? FAKE_EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitBranch, UNKNOWN_GIT_BRANCH);
}
}
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', '') ?? FAKE_EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitRepoName, UNKNOWN_GIT_REPO_NAME);
}
}
return raw;
}
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, FAKE_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, FAKE_EMPTY);
const workspaceFolder = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri);
const workspaceFolderName = workspaceFolder?.name ?? noWorkspaceFound;
const workspaceName = workspace.name?.replace(REPLACE_KEYS.VSCodeWorkspace, EMPTY) ?? workspaceFolderName;
const workspaceAndFolder = `${workspaceName}${
workspaceFolderName === FAKE_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)}`);
}
try {
raw = await fileDetails(raw, window.activeTextEditor.document, window.activeTextEditor.selection);
} catch (error) {
log(LogLevel.Error, `Failed to generate file details: ${error as string}`);
}
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;
}
export async function activity(previous: ActivityPayload = {}) {
@ -132,124 +253,3 @@ export async function activity(previous: ActivityPayload = {}) {
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, FAKE_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, FAKE_EMPTY);
const workspaceFolder = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri);
const workspaceFolderName = workspaceFolder?.name ?? noWorkspaceFound;
const workspaceName = workspace.name?.replace(REPLACE_KEYS.VSCodeWorkspace, EMPTY) ?? workspaceFolderName;
const workspaceAndFolder = `${workspaceName}${
workspaceFolderName === FAKE_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)}`);
}
try {
raw = await fileDetails(raw, window.activeTextEditor.document, window.activeTextEditor.selection);
} catch (error) {
log(LogLevel.Error, `Failed to generate file details: ${error as string}`);
}
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();
if (raw.includes(REPLACE_KEYS.TotalLines)) {
raw = raw.replace(REPLACE_KEYS.TotalLines, document.lineCount.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: number;
try {
({ size } = await workspace.fs.stat(document.uri));
} catch {
size = document.getText().length;
}
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]}`,
);
}
const git = await getGit();
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 ?? FAKE_EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitBranch, UNKNOWN_GIT_BRANCH);
}
}
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', '') ?? FAKE_EMPTY,
);
} else {
raw = raw.replace(REPLACE_KEYS.GitRepoName, UNKNOWN_GIT_REPO_NAME);
}
}
return raw;
}

View file

@ -7,7 +7,7 @@ export const KNOWN_LANGUAGES: { language: string; image: string }[] = LANG.KNOWN
export const EMPTY = '' as const;
export const FAKE_EMPTY = '\u200b\u200b' as const;
export const FILE_SIZES = [' bytes', 'kb', 'mb', 'gb', 'tb'] as const;
export const FILE_SIZES = [' bytes', 'KB', 'MB', 'GB', 'TB'] as const;
export const IDLE_IMAGE_KEY = 'vscode-big' as const;
export const DEBUG_IMAGE_KEY = 'debug' as const;

View file

@ -158,7 +158,7 @@
"/\\.(exs|l?eex)$/i": { "image": "elixir" },
"/^mix\\.(exs?|lock)$/i": { "image": "elixir" },
".elm": { "image": "elm" },
".env": { "image": "env" },
"/\\.env(.*)?/i": { "image": "env" },
".erl": { "image": "erlang" },
".beam": { "image": "erlang" },
".hrl": { "image": "erlang" },
@ -234,6 +234,7 @@
".sexp": { "image": "lisp" },
".ss": { "image": "lisp" },
".scm": { "image": "lisp" },
".liquid": { "image": "liquid" },
".ls": { "image": "livescript" },
".log": { "image": "log" },
".lua": { "image": "lua" },
@ -356,6 +357,7 @@
"/^(Snakefile|WATCHLISTS)$/": { "image": "python" },
"/^wscript$/": { "image": "python" },
"/\\.(r|Rprofile|rsx|rd)$/i": { "image": "r" },
".rkt": { "image": "racket" },
"/\\.res?i?$/i": { "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" },
@ -387,6 +389,7 @@
".tcsh": { "image": "shell" },
".csh": { "image": "shell" },
".sk": { "image": "skript" },
".sol": { "image": "solidity" },
".sqf": { "image": "sqf" },
"/\\.(my)?sql$/i": { "image": "sql" },
".ddl": { "image": "sql" },
@ -418,6 +421,7 @@
"/\\.i?nfo$/i": { "image": "text" },
".msg": { "image": "text" },
"/\\.(utxt|utf8)$/i": { "image": "text" },
"/\\.(tfvars|tf)$/i": { "image": "terraform" },
".toml": { "image": "toml" },
".travis.yml": { "image": "travis" },
".ts": { "image": "typescript" },

View file

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
const { Client } = require('discord-rpc'); // eslint-disable-line
import { commands, ExtensionContext, StatusBarAlignment, StatusBarItem, window, workspace, debug } from 'vscode';
import throttle from 'lodash-es/throttle';
@ -10,14 +12,16 @@ import { getConfig, getGit } from './util';
const statusBarIcon: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left);
statusBarIcon.text = '$(pulse) Connecting to Discord...';
// eslint-disable-next-line
let rpc = new Client({ transport: 'ipc' });
const config = getConfig();
let state = {};
let idle: NodeJS.Timeout | undefined;
let listeners: { dispose(): any }[] = [];
let listeners: { dispose: () => any }[] = [];
export function cleanUp() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
listeners.forEach((listener) => listener.dispose());
listeners = [];
}
@ -49,9 +53,9 @@ async function login() {
listeners.push(onChangeActiveTextEditor, onChangeTextDocument, onStartDebugSession, onTerminateDebugSession);
});
rpc.on('disconnected', async () => {
rpc.on('disconnected', () => {
cleanUp();
await rpc.destroy();
rpc.destroy();
statusBarIcon.text = '$(pulse) Reconnect to Discord';
statusBarIcon.command = 'discord.reconnect';
});
@ -61,9 +65,9 @@ async function login() {
} catch (error) {
log(LogLevel.Error, `Encountered following error while trying to login:\n${error as string}`);
cleanUp();
await rpc.destroy();
rpc.destroy();
if (!config[CONFIG_KEYS.SuppressNotifications]) {
// @ts-ignore
// @ts-expect-error
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}`);
}

24
src/git.d.ts vendored
View file

@ -1,7 +1,16 @@
/*---------------------------------------------------------------------------------------------
/**
* Repo: https://github.com/microsoft/vscode
* File: extensions/git/src/api/git.d.ts
* Branch: release/1.59
* Commit: e3829dc2477186593fe79d649f909119f5dc5913
* Date: 2021.11.06
*/
/* ---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
* Licensed under the MIT License. See License in the project root for license information.
* --------------------------------------------------------------------------------------------
*/
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
export { ProviderResult } from 'vscode';
@ -138,6 +147,14 @@ export interface CommitOptions {
requireUserConfig?: boolean;
}
export interface FetchOptions {
remote?: string;
ref?: string;
all?: boolean;
prune?: boolean;
depth?: number;
}
export interface BranchQuery {
readonly remote?: boolean;
readonly pattern?: string;
@ -195,6 +212,7 @@ export interface Repository {
removeRemote(name: string): Promise<void>;
renameRemote(name: string, newName: string): Promise<void>;
fetch(options?: FetchOptions): Promise<void>;
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
pull(unshallow?: boolean): Promise<void>;
push(remoteName?: string, branchName?: string, setUpstream?: boolean, force?: ForcePushMode): Promise<void>;

View file

@ -2,7 +2,7 @@ import { basename } from 'path';
import { TextDocument, workspace, extensions, WorkspaceConfiguration } from 'vscode';
import { KNOWN_EXTENSIONS, KNOWN_LANGUAGES } from './constants';
import { API, GitExtension } from './git';
import type { API, GitExtension } from './git';
import { log, LogLevel } from './logger';
let git: API | null | undefined;

View file

@ -7,11 +7,14 @@
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.mjs",
"**/*.jsx",
"**/*.test.ts",
"**/*.test.js",
"**/*.test.mjs",
"**/*.spec.ts",
"**/*.spec.js"
"**/*.spec.js",
"**/*.spec.mjs"
],
"exclude": []
}

View file

@ -1,22 +1,42 @@
{
// Mapped from https://www.typescriptlang.org/tsconfig
"compilerOptions": {
// Type Checking
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"useUnknownInCatchVariables": true,
// Modules
"module": "CommonJS",
"moduleResolution": "node",
"declaration": false,
"removeComments": false,
"alwaysStrict": true,
"pretty": true,
"target": "ES2019",
"module": "commonjs",
"lib": ["ESNext"],
"outDir": "dist",
"sourceMap": true,
"inlineSources": true,
"incremental": true,
"noEmitHelpers": true,
"resolveJsonModule": true,
// Emit
"declaration": true,
"importHelpers": true,
"skipLibCheck": true,
"importsNotUsedAsValues": "error",
"inlineSources": true,
"newLine": "lf",
"noEmitHelpers": true,
"outDir": "dist",
"preserveConstEnums": true,
"removeComments": false,
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true
"forceConsistentCasingInFileNames": true,
// Language and Environment
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["ESNext"],
"target": "ES2021",
"useDefineForClassFields": true
}
}