mirror of
https://gitlab.com/aviortheking/code-stats-vscode.git
synced 2025-04-22 02:42:11 +00:00
feat: Allow the plugin to work under the browser context
Signed-off-by: Avior <f.bouillon@aptatio.com>
This commit is contained in:
parent
e42e5271db
commit
cef14d1038
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
.DS_Store
|
||||
out
|
||||
node_modules
|
||||
.vscode-test/
|
||||
.vscode-test*/
|
||||
*.vsix
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -6,5 +6,8 @@
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
|
||||
// username for internal browser tests
|
||||
"codestats.username": "Aviortheking"
|
||||
}
|
14
esbuild.js
Normal file
14
esbuild.js
Normal file
@ -0,0 +1,14 @@
|
||||
const { NodeModulesPolyfillPlugin } = require('@esbuild-plugins/node-modules-polyfill')
|
||||
const { build } = require('esbuild')
|
||||
build({
|
||||
entryPoints: ['./src/code-stats.ts'],
|
||||
plugins: [NodeModulesPolyfillPlugin()],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
target: ['es2016', 'chrome90', 'firefox78', 'safari14', 'edge90'],
|
||||
external: ['vscode', 'node-fetch'],
|
||||
outfile: 'out/browser.js',
|
||||
format: 'cjs',
|
||||
platform: 'node'
|
||||
})
|
3268
package-lock.json
generated
3268
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -31,6 +31,7 @@
|
||||
"*"
|
||||
],
|
||||
"main": "./out/src/code-stats",
|
||||
"browser": "./out/browser",
|
||||
"extensionKind": [
|
||||
"ui",
|
||||
"workspace"
|
||||
@ -66,19 +67,25 @@
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "tsc -p ./",
|
||||
"compile": "tsc -watch -p ./"
|
||||
"compile": "tsc -watch -p ./",
|
||||
"test:browser": "vscode-test-web --extensionDevelopmentPath=. .",
|
||||
"compile:browser": "node esbuild.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^13.9.8",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/vscode": "^1.43.2",
|
||||
"@vscode/test-web": "^0.0.34",
|
||||
"esbuild": "^0.17.3",
|
||||
"growl": "^1.10.5",
|
||||
"mocha": "^7.1.1",
|
||||
"typescript": "^3.8.3",
|
||||
"vscode-test": "^1.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"lodash.template": "^4.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash.template": "^4.5.0",
|
||||
"node-fetch": "^2.6.8"
|
||||
}
|
||||
}
|
||||
|
@ -1,102 +1,105 @@
|
||||
import { Pulse } from "./pulse";
|
||||
import { getISOTimestamp, getLanguageName } from "./utils";
|
||||
import * as axios from "axios";
|
||||
import type fetch from 'node-fetch'
|
||||
import { Pulse } from "./pulse"
|
||||
import { getISOTimestamp, getLanguageName } from "./utils"
|
||||
|
||||
// Please do not look at this
|
||||
let realFetch: typeof fetch
|
||||
// @ts-expect-error VSCode is not including fetch everytime
|
||||
if (typeof fetch === 'undefined') {
|
||||
realFetch = require('node-fetch')
|
||||
} else {
|
||||
// @ts-expect-error VSCode is not including fetch everytime
|
||||
realFetch = fetch
|
||||
}
|
||||
|
||||
export class CodeStatsAPI {
|
||||
private API_KEY = null;
|
||||
private USER_NAME = null;
|
||||
private UPDATE_URL = "https://codestats.net/api/";
|
||||
|
||||
private axios = null;
|
||||
private API_KEY = null;
|
||||
private USER_NAME = null;
|
||||
private UPDATE_URL = "https://codestats.net/api";
|
||||
private headers: Record<string, string> = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
constructor(apiKey: string, apiURL: string, userName: string) {
|
||||
this.updateSettings(apiKey, apiURL, userName);
|
||||
}
|
||||
constructor(apiKey: string, apiURL: string, userName: string) {
|
||||
this.updateSettings(apiKey, apiURL, userName);
|
||||
}
|
||||
|
||||
public updateSettings( apiKey: string, apiURL: string, userName: string) {
|
||||
public updateSettings(apiKey: string, apiURL: string, userName: string) {
|
||||
|
||||
this.API_KEY = apiKey;
|
||||
this.UPDATE_URL = apiURL;
|
||||
this.USER_NAME = userName;
|
||||
this.API_KEY = apiKey;
|
||||
this.UPDATE_URL = apiURL;
|
||||
this.USER_NAME = userName;
|
||||
|
||||
if (
|
||||
this.API_KEY === null ||
|
||||
this.API_KEY === undefined ||
|
||||
this.API_KEY === ""
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.API_KEY === null ||
|
||||
this.API_KEY === undefined ||
|
||||
this.API_KEY === ""
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.axios = axios.default.create({
|
||||
baseURL: this.UPDATE_URL,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"X-API-Token": this.API_KEY,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
}
|
||||
this.headers["X-API-Token"] = this.API_KEY
|
||||
}
|
||||
|
||||
public sendUpdate(pulse: Pulse): axios.AxiosPromise {
|
||||
// If we did not have API key, don't try to update
|
||||
if (this.axios === null) {
|
||||
return null;
|
||||
}
|
||||
public async sendUpdate(pulse: Pulse): Promise<any> {
|
||||
// If we did not have API key, don't try to update
|
||||
if (this.API_KEY === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:typedef
|
||||
const data = new ApiJSON(new Date());
|
||||
// tslint:disable-next-line:typedef
|
||||
const data = new ApiJSON(new Date());
|
||||
|
||||
for (let lang of pulse.xps.keys()) {
|
||||
let languageName: string = getLanguageName(lang);
|
||||
let xp: number = pulse.getXP(lang);
|
||||
data.xps.push(new ApiXP(languageName, xp));
|
||||
}
|
||||
for (let lang of pulse.xps.keys()) {
|
||||
let languageName: string = getLanguageName(lang);
|
||||
let xp: number = pulse.getXP(lang);
|
||||
data.xps.push(new ApiXP(languageName, xp));
|
||||
}
|
||||
|
||||
let json: string = JSON.stringify(data);
|
||||
console.log(`JSON: ${json}`);
|
||||
let json: string = JSON.stringify(data);
|
||||
console.log(`JSON: ${json}`);
|
||||
|
||||
return this.axios
|
||||
.post("my/pulses", json)
|
||||
.then(response => {
|
||||
console.log(response);
|
||||
})
|
||||
.then(() => {
|
||||
pulse.reset();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
try {
|
||||
const response = await realFetch(`${this.UPDATE_URL}/my/pulses`, {
|
||||
method: 'POST',
|
||||
body: json,
|
||||
headers: this.headers
|
||||
})
|
||||
console.log(response)
|
||||
pulse.reset()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
public getProfile(): axios.AxiosPromise {
|
||||
return this.axios
|
||||
.get(`users/${this.USER_NAME}`)
|
||||
.then(response => {
|
||||
return response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
public async getProfile(): Promise<any> {
|
||||
try {
|
||||
const resp = await realFetch(`${this.UPDATE_URL}/users/${this.USER_NAME}`)
|
||||
const response = await resp.json()
|
||||
return response
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ApiJSON {
|
||||
constructor(date: Date) {
|
||||
this.coded_at = getISOTimestamp(new Date());
|
||||
this.xps = new Array<ApiXP>();
|
||||
}
|
||||
constructor(date: Date) {
|
||||
this.coded_at = getISOTimestamp(new Date());
|
||||
this.xps = new Array<ApiXP>();
|
||||
}
|
||||
|
||||
coded_at: string;
|
||||
xps: Array<ApiXP>;
|
||||
coded_at: string;
|
||||
xps: Array<ApiXP>;
|
||||
}
|
||||
|
||||
class ApiXP {
|
||||
constructor(language: string, xp: number) {
|
||||
this.language = language;
|
||||
this.xp = xp;
|
||||
}
|
||||
constructor(language: string, xp: number) {
|
||||
this.language = language;
|
||||
this.xp = xp;
|
||||
}
|
||||
|
||||
language: string;
|
||||
xp: number;
|
||||
language: string;
|
||||
xp: number;
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
"use strict";
|
||||
import { ExtensionContext } from "vscode";
|
||||
import { XpCounter } from "./xp-counter";
|
||||
import { ExtensionContext } from "vscode"
|
||||
import { XpCounter } from "./xp-counter"
|
||||
|
||||
export function activate(context: ExtensionContext): void {
|
||||
let controller: XpCounter = new XpCounter(context);
|
||||
context.subscriptions.push(controller);
|
||||
let controller: XpCounter = new XpCounter(context);
|
||||
context.subscriptions.push(controller);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
|
@ -1,99 +1,94 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri } from "vscode";
|
||||
import { CodeStatsAPI } from "./code-stats-api";
|
||||
import * as path from 'path';
|
||||
import { CancellationToken, Event, ExtensionContext, TextDocumentContentProvider, Uri, workspace } from "vscode"
|
||||
import { CodeStatsAPI } from "./code-stats-api"
|
||||
import profileHtmlEex from './profile.html.eex'
|
||||
|
||||
import template = require('lodash.template');
|
||||
|
||||
export class ProfileProvider implements TextDocumentContentProvider {
|
||||
onDidChange?: Event<Uri>;
|
||||
onDidChange?: Event<Uri>;
|
||||
|
||||
api: CodeStatsAPI;
|
||||
context: ExtensionContext;
|
||||
api: CodeStatsAPI;
|
||||
context: ExtensionContext;
|
||||
|
||||
constructor(context: ExtensionContext, api: CodeStatsAPI) {
|
||||
this.context = context;
|
||||
this.api = api;
|
||||
}
|
||||
constructor(context: ExtensionContext, api: CodeStatsAPI) {
|
||||
this.context = context;
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string> {
|
||||
provideTextDocumentContent(uri: Uri, token: CancellationToken): string | Thenable<string> {
|
||||
|
||||
if( token.isCancellationRequested )
|
||||
return;
|
||||
if (token.isCancellationRequested) return;
|
||||
|
||||
const LEVEL_FACTOR = 0.025;
|
||||
const LEVEL_FACTOR = 0.025;
|
||||
|
||||
function getLevel(xp: number): number {
|
||||
return Math.floor(Math.sqrt(xp) * LEVEL_FACTOR);
|
||||
}
|
||||
function getLevel(xp: number): number {
|
||||
return Math.floor(Math.sqrt(xp) * LEVEL_FACTOR);
|
||||
}
|
||||
|
||||
function getNextLevelXp(level: number): number {
|
||||
return Math.pow(Math.ceil((level + 1) / LEVEL_FACTOR), 2);
|
||||
}
|
||||
function getNextLevelXp(level: number): number {
|
||||
return Math.pow(Math.ceil((level + 1) / LEVEL_FACTOR), 2);
|
||||
}
|
||||
|
||||
function getLevelProgress(xp: number, new_xp: number): number[] {
|
||||
let level = getLevel(xp);
|
||||
let curLevelXp = getNextLevelXp(level - 1);
|
||||
let nextLevelXp = getNextLevelXp(level);
|
||||
function getLevelProgress(xp: number, new_xp: number): number[] {
|
||||
let level = getLevel(xp);
|
||||
let curLevelXp = getNextLevelXp(level - 1);
|
||||
let nextLevelXp = getNextLevelXp(level);
|
||||
|
||||
let haveXp = xp - curLevelXp;
|
||||
|
||||
let needXp = nextLevelXp - curLevelXp;
|
||||
let haveXp = xp - curLevelXp;
|
||||
|
||||
let needXp = nextLevelXp - curLevelXp;
|
||||
|
||||
let xpP = Math.round(haveXp * 100.0 / needXp);
|
||||
let nxpP = Math.round(new_xp * 100.0 / needXp);
|
||||
return [ xpP, nxpP ];
|
||||
}
|
||||
let xpP = Math.round(haveXp * 100.0 / needXp);
|
||||
let nxpP = Math.round(new_xp * 100.0 / needXp);
|
||||
return [ xpP, nxpP ];
|
||||
}
|
||||
|
||||
function getSortedArray(obj: any): any[] {
|
||||
function getSortedArray(obj: any): any[] {
|
||||
let items = [];
|
||||
|
||||
let items = [];
|
||||
for (const prop in obj) {
|
||||
let item = obj[prop];
|
||||
let percents = getLevelProgress(item.xps, item.new_xps);
|
||||
items.push(
|
||||
{
|
||||
name: prop,
|
||||
level: getLevel(item.xps),
|
||||
xp: item.xps,
|
||||
new_xp: item.new_xps,
|
||||
progress: percents[0],
|
||||
new_progress: percents[1]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return items.sort( (a,b) => {return b.xp - a.xp;});
|
||||
}
|
||||
|
||||
for( let prop in obj) {
|
||||
let item = obj[prop];
|
||||
let percents = getLevelProgress(item.xps, item.new_xps);
|
||||
items.push(
|
||||
{
|
||||
name: prop,
|
||||
level: getLevel(item.xps),
|
||||
xp: item.xps,
|
||||
new_xp: item.new_xps,
|
||||
progress: percents[0],
|
||||
new_progress: percents[1]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return items.sort( (a,b) => {return b.xp - a.xp;});
|
||||
}
|
||||
return this.api.getProfile().then(async (profile) => {
|
||||
|
||||
return this.api.getProfile().then(profile => {
|
||||
if( profile === null )
|
||||
{
|
||||
return `<h1>Can't fetch profile. Please try again later</h1> Make sure <strong>codestats.username</strong> setting is set to correct user name.`;
|
||||
}
|
||||
|
||||
if( profile === null )
|
||||
{
|
||||
return `<h1>Can't fetch profile. Please try again later</h1> Make sure <strong>codestats.username</strong> setting is set to correct user name.`;
|
||||
}
|
||||
// don't look at this file please
|
||||
let htmlTemplate = profileHtmlEex
|
||||
|
||||
profile["level"] = getLevel(profile["total_xp"]);
|
||||
|
||||
let htmlTemplate = fs.readFileSync(this.context.asAbsolutePath("assets/profile.html.eex"));
|
||||
|
||||
profile["level"] = getLevel(profile["total_xp"]);
|
||||
let percents = getLevelProgress(profile["total_xp"], profile["new_xp"]);
|
||||
|
||||
let percents = getLevelProgress(profile["total_xp"], profile["new_xp"]);
|
||||
profile["progress"] = percents[0];
|
||||
profile["new_progress"] = percents[1];
|
||||
|
||||
profile["progress"] = percents[0];
|
||||
profile["new_progress"] = percents[1];
|
||||
let languages = getSortedArray(profile["languages"]);
|
||||
let machines = getSortedArray(profile["machines"]);
|
||||
|
||||
let html = template(htmlTemplate);
|
||||
|
||||
let languages = getSortedArray(profile["languages"]);
|
||||
let machines = getSortedArray(profile["machines"]);
|
||||
|
||||
let html = template(htmlTemplate);
|
||||
|
||||
const stylePath = Uri.file(path.join(this.context.extensionPath, 'assets', 'profile.css'));
|
||||
const styleSrc = stylePath.with({scheme: 'vscode-resource'});
|
||||
return html({profile: profile, languages: languages, machines: machines});
|
||||
|
||||
return html({profile: profile, languages: languages, machines: machines, style: styleSrc});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
191
src/profile.html.eex.ts
Normal file
191
src/profile.html.eex.ts
Normal file
@ -0,0 +1,191 @@
|
||||
export default `
|
||||
<style>
|
||||
/* Dark theme color palette */
|
||||
.vscode-dark {
|
||||
--color-language: #dbd5b9;
|
||||
--color-primary: #E3C23D;
|
||||
--color-secondary: #5D5535;
|
||||
--color-bar-background: #303030;
|
||||
--color-bar-border: #424242;
|
||||
}
|
||||
|
||||
/* Light theme color palette */
|
||||
.vscode-light {
|
||||
--color-language: #1B2334;
|
||||
--color-primary: #5D78B3;
|
||||
--color-secondary: #354567;
|
||||
--color-bar-background: #939DB3;
|
||||
--color-bar-border: #697080;
|
||||
}
|
||||
|
||||
.profile {
|
||||
color: var(--color-language);
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
font-size: 75%;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.language-progress {
|
||||
float: left;
|
||||
position: relative;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.language-progress .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 120px;
|
||||
background-color: var(--color-bar-border);
|
||||
color: var(--color-language);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.language-progress:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.language-progress svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
circle {
|
||||
stroke-width: 5;
|
||||
fill:transparent;
|
||||
}
|
||||
|
||||
circle.backg {
|
||||
stroke: var(--color-bar-background);
|
||||
}
|
||||
|
||||
circle.newxp {
|
||||
stroke: var(--color-primary);
|
||||
}
|
||||
|
||||
circle.oldxp {
|
||||
stroke: var(--color-secondary);
|
||||
}
|
||||
|
||||
.language {
|
||||
padding-top: 2rem;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
color: var(--color-language);
|
||||
}
|
||||
|
||||
.language span {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.machines {
|
||||
clear: both;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
.machine {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 20px;
|
||||
margin-bottom: 20px;
|
||||
background-color: var(--color-bar-background);
|
||||
border-radius: 4px;
|
||||
border: solid 1px;
|
||||
border-color: var(--color-bar-border);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
float: left;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.progress-bar-new {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<div class="profile">
|
||||
<h3> \${profile.user} <sup>\${profile.level}</sup> \${profile.total_xp} xp
|
||||
<% if( profile.new_xp > 0 ) { %>
|
||||
<sup>+<%= profile.new_xp %></sup>
|
||||
<% } %>
|
||||
</h3>
|
||||
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style='width:\${profile.progress}%;'>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-new" style='width:\${profile.new_progress}%;'>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="languages">
|
||||
<% for( let l in languages) { %>
|
||||
<div class="language-progress">
|
||||
<span class="tooltiptext">
|
||||
<strong><%=languages[l].xp %> xp</strong>
|
||||
<% if( languages[l].new_xp > 0 ) { %>
|
||||
<sup>+<%= languages[l].new_xp %></sup>
|
||||
<% } %>
|
||||
</span>
|
||||
<svg viewBox="0 0 100 100">
|
||||
<circle class="backg" cx="50" cy="50" r="45" ></circle>
|
||||
<circle class="newxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress) * 282.6 / 100) }'></circle>
|
||||
<circle class="oldxp" cx="50" cy="50" r="45" stroke-dasharray="282.6" stroke-dashoffset='\${ ((100-languages[l].progress + languages[l].new_progress) * 282.6 / 100) }'></circle>
|
||||
</svg>
|
||||
<div class="language">
|
||||
<%= languages[l].name %>
|
||||
<span>
|
||||
<%= languages[l].level %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<p/>
|
||||
|
||||
<div class="machines">
|
||||
<% for( let m in machines) { %>
|
||||
<div class="machine">
|
||||
<strong>
|
||||
<%= machines[m].name %>
|
||||
</strong>
|
||||
<sup> <%= machines[m].level %> </sup>
|
||||
<%= machines[m].xp %> xp
|
||||
<% if( machines[m].new_xp > 0 ) { %>
|
||||
<sup>+<%= machines[m].new_xp %></sup>
|
||||
<% } %>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style='width:\${machines[m].progress}%;'>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-new" style='width:\${machines[m].new_progress}%;'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<div>`
|
@ -1,22 +1,12 @@
|
||||
import * as path from 'path'
|
||||
import {
|
||||
Disposable,
|
||||
workspace,
|
||||
window,
|
||||
Uri,
|
||||
ViewColumn,
|
||||
commands,
|
||||
StatusBarItem,
|
||||
TextDocument,
|
||||
StatusBarAlignment,
|
||||
TextDocumentChangeEvent,
|
||||
WorkspaceConfiguration,
|
||||
ExtensionContext
|
||||
} from "vscode";
|
||||
import { Pulse } from "./pulse";
|
||||
import { CodeStatsAPI } from "./code-stats-api";
|
||||
import { ProfileProvider } from "./profile-provider";
|
||||
import * as path from 'path';
|
||||
|
||||
commands, Disposable, ExtensionContext, StatusBarAlignment, StatusBarItem,
|
||||
TextDocument, TextDocumentChangeEvent, Uri,
|
||||
ViewColumn, window, workspace, WorkspaceConfiguration
|
||||
} from "vscode"
|
||||
import { CodeStatsAPI } from "./code-stats-api"
|
||||
import { ProfileProvider } from "./profile-provider"
|
||||
import { Pulse } from "./pulse"
|
||||
export class XpCounter {
|
||||
private combinedDisposable: Disposable;
|
||||
private statusBarItem: StatusBarItem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user