mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
add basic analytics
This commit is contained in:
10
extension.js
10
extension.js
@@ -2,6 +2,8 @@
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
const path = require('path');
|
||||
const vscode = require('vscode');
|
||||
const analytics = require('./langserver/analytics');
|
||||
const package_json = require('./package.json');
|
||||
const { LanguageClient, TransportKind, } = require('vscode-languageclient');
|
||||
const { AndroidContentProvider } = require('./src/contentprovider');
|
||||
const { openLogcatWindow } = require('./src/logcat');
|
||||
@@ -43,6 +45,8 @@ async function createLanguageClient(context) {
|
||||
}
|
||||
const sourceFiles = (await vscode.workspace.findFiles(`${globSearchRoot}**/*.java`, null, 1000, null)).map(uri => uri.toString());
|
||||
|
||||
const mpids = analytics.getIDs(context);
|
||||
|
||||
// Options to control the language client
|
||||
/** @type {import('vscode-languageclient').LanguageClientOptions} */
|
||||
let clientOptions = {
|
||||
@@ -53,8 +57,11 @@ async function createLanguageClient(context) {
|
||||
initializationOptions: {
|
||||
// extensionPath points to the root of the extension (the folder where this file is)
|
||||
extensionPath: context.extensionPath,
|
||||
mpuid: mpids.uid,
|
||||
mpsid: mpids.sid,
|
||||
initialSettings: config,
|
||||
sourceFiles,
|
||||
vscodeVersion: vscode.version,
|
||||
workspaceFolders: (vscode.workspace.workspaceFolders || []).map(z => z.uri.toString()),
|
||||
},
|
||||
synchronize: {
|
||||
@@ -107,6 +114,9 @@ function activate(context) {
|
||||
/* Only the logcat stuff is configured here. The debugger is launched from src/debugMain.js */
|
||||
AndroidContentProvider.register(context, vscode.workspace);
|
||||
|
||||
const mpids = analytics.getIDs(context);
|
||||
analytics.init(undefined, mpids.uid, mpids.sid, package_json, { vscode_version: vscode.version });
|
||||
|
||||
createLanguageClient(context).then(client => {
|
||||
languageClient = client;
|
||||
refreshLanguageServerEnabledState();
|
||||
|
||||
134
langserver/analytics.js
Normal file
134
langserver/analytics.js
Normal file
@@ -0,0 +1,134 @@
|
||||
let mp;
|
||||
/** @type {string} */
|
||||
let uid;
|
||||
/** @type {string} */
|
||||
let sid;
|
||||
/** @type {Map<string,[number,number]>} */
|
||||
const timeLabels = new Map();
|
||||
let session_start = Date.now();
|
||||
|
||||
/**
|
||||
* @param {string} t
|
||||
* @param {string} u
|
||||
* @param {string} s
|
||||
* @param {{name:string,version:string}} package_json
|
||||
*/
|
||||
function init(t = '0cca95950055c6553804a46ce7e3df18', u, s, package_json, props) {
|
||||
if (mp) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mp = require('mixpanel').init(t);
|
||||
}
|
||||
catch {}
|
||||
uid = u;
|
||||
sid = s;
|
||||
|
||||
const os = require('os');
|
||||
event(`${package_json.name}-start`, {
|
||||
extension: package_json.name,
|
||||
ext_version: package_json.version,
|
||||
arch: process.arch,
|
||||
cpus: os.cpus().length,
|
||||
mem: (os.totalmem() / 1e6)|0,
|
||||
platform: process.platform,
|
||||
node_version: process.version,
|
||||
release: os.release(),
|
||||
...props
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {*} [properties]
|
||||
*/
|
||||
function event(eventName, properties) {
|
||||
if (!mp) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (uid) {
|
||||
mp.track(eventName, {
|
||||
distinct_id: uid,
|
||||
session_id: sid,
|
||||
session_length: Math.trunc((Date.now() - session_start) / 60e3),
|
||||
...properties,
|
||||
});
|
||||
} else {
|
||||
mp.track(eventName);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
*/
|
||||
function time(label) {
|
||||
if (!label || timeLabels.has(label)) {
|
||||
return;
|
||||
}
|
||||
timeLabels.set(label, process.hrtime());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
* @param {'ns'|'us'|'ms'|'s'} time_unit
|
||||
* @param {*} [additionalProps]
|
||||
*/
|
||||
function timeEnd(label, time_unit = 'ms', additionalProps = {}) {
|
||||
if (!label) {
|
||||
return;
|
||||
}
|
||||
const startTime = timeLabels.get(label);
|
||||
timeLabels.delete(label);
|
||||
if (!Array.isArray(startTime)) {
|
||||
return;
|
||||
}
|
||||
const elapsed = process.hrtime(startTime);
|
||||
const count = time_unit === 's' ? elapsed[0] : ((elapsed[0]*1e9) + elapsed[1]);
|
||||
const divs = {
|
||||
ns: 1, us: 1e3, ms: 1e6, s: 1
|
||||
}
|
||||
const props = {
|
||||
[`${label}-elapsed`]: Math.trunc(count / (divs[time_unit] || 1)),
|
||||
[`${label}-elapsed_unit`]: time_unit,
|
||||
...additionalProps,
|
||||
}
|
||||
event(label, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('vscode').ExtensionContext} context
|
||||
*/
|
||||
function getIDs(context) {
|
||||
if (!context || !context.globalState) {
|
||||
return {
|
||||
uid: '', sid: ''
|
||||
};
|
||||
}
|
||||
let uuidv4 = () => {
|
||||
try {
|
||||
uuidv4 = require('uuid').v4;
|
||||
return uuidv4();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
let u = uid || (uid = context.globalState.get('mix-panel-id'));
|
||||
if (typeof u !== 'string' || u.length > 36) {
|
||||
u = uid = uuidv4();
|
||||
context.globalState.update('mix-panel-id', u);
|
||||
}
|
||||
let s = sid || (sid = uuidv4());
|
||||
return {
|
||||
uid: u,
|
||||
sid: s,
|
||||
}
|
||||
}
|
||||
|
||||
exports.init = init;
|
||||
exports.event = event;
|
||||
exports.time = time;
|
||||
exports.timeEnd = timeEnd;
|
||||
exports.getIDs = getIDs;
|
||||
@@ -5,6 +5,7 @@ const { SourceType } = require('./java/source-types');
|
||||
const { indexAt } = require('./document');
|
||||
const { formatDoc } = require('./doc-formatter');
|
||||
const { trace } = require('./logging');
|
||||
const { event } = require('./analytics');
|
||||
|
||||
/**
|
||||
* Case-insensitive sort routines
|
||||
@@ -276,6 +277,8 @@ let defaultCompletionTypes = null;
|
||||
/** @type {Map<string,CEIType>} */
|
||||
let lastCompletionTypeMap = null;
|
||||
|
||||
let completionRequestCount = 0;
|
||||
|
||||
function initDefaultCompletionTypes(lib) {
|
||||
defaultCompletionTypes = {
|
||||
instances: 'this super'.split(' ').map(t => ({
|
||||
@@ -365,6 +368,11 @@ async function getCompletionItems(params, liveParsers, androidLibrary) {
|
||||
}
|
||||
}
|
||||
|
||||
completionRequestCount += 1;
|
||||
if ((completionRequestCount === 1) || (completionRequestCount === 5) || ((completionRequestCount % 25) === 0)) {
|
||||
event('completion-requests', { comp_req_count: completionRequestCount });
|
||||
}
|
||||
|
||||
let parsed = docinfo.parsed;
|
||||
|
||||
// save the typemap associated with this parsed state - we use this when resolving
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { CEIType, loadJavaLibraryCacheFile } = require('java-mti');
|
||||
const analytics = require('../analytics');
|
||||
const { trace, time, timeEnd } = require('../logging');
|
||||
|
||||
/**
|
||||
@@ -9,6 +10,7 @@ const { trace, time, timeEnd } = require('../logging');
|
||||
* @returns {Promise<Map<string,CEIType>>}
|
||||
*/
|
||||
async function loadAndroidSystemLibrary(extensionPath, additional_libs) {
|
||||
analytics.time('android-library-load');
|
||||
time('android-library-load');
|
||||
let library;
|
||||
try {
|
||||
@@ -25,6 +27,7 @@ async function loadAndroidSystemLibrary(extensionPath, additional_libs) {
|
||||
library = typemap;
|
||||
} finally {
|
||||
timeEnd('android-library-load');
|
||||
analytics.timeEnd('android-library-load', 'ms', { libs: additional_libs, typecount: library ? library.size : 0 });
|
||||
}
|
||||
return library;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ const { Method } = require('java-mti');
|
||||
const { indexAt } = require('./document');
|
||||
const { formatDoc } = require('./doc-formatter');
|
||||
const { trace } = require('./logging');
|
||||
const { event } = require('./analytics');
|
||||
|
||||
let methodsigRequestCount = 0;
|
||||
|
||||
/**
|
||||
* Retrieve method signature information
|
||||
@@ -36,6 +39,11 @@ async function getSignatureHelp(request, liveParsers) {
|
||||
// wait for any active edits to complete
|
||||
await docinfo.reparseWaiter;
|
||||
|
||||
methodsigRequestCount += 1;
|
||||
if ((methodsigRequestCount === 1) || (methodsigRequestCount === 5) || ((methodsigRequestCount % 25) === 0)) {
|
||||
event('method-sig-requests', { methsig_req_count: methodsigRequestCount });
|
||||
}
|
||||
|
||||
// locate the token at the requested position
|
||||
const index = indexAt(request.position, docinfo.content);
|
||||
const token = docinfo.parsed.unit.getTokenAt(index);
|
||||
|
||||
5
langserver/package-lock.json
generated
5
langserver/package-lock.json
generated
@@ -301,6 +301,11 @@
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz",
|
||||
"integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q=="
|
||||
},
|
||||
"vscode-jsonrpc": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"java-mti": "adelphes/java-mti#d0e1e45",
|
||||
"mixpanel": "^0.11.0",
|
||||
"uuid": "^8.2.0",
|
||||
"vscode-languageserver": "6.1.1",
|
||||
"vscode-languageserver-textdocument": "1.0.1",
|
||||
"vscode-uri": "2.1.2"
|
||||
|
||||
@@ -19,6 +19,10 @@ const { clearDefaultCompletionEntries, getCompletionItems, resolveCompletionItem
|
||||
const { getSignatureHelp } = require('./method-signatures');
|
||||
const { FileURIMap, JavaDocInfo, indexAt, reparse } = require('./document');
|
||||
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const analytics = require('./analytics');
|
||||
const package_json = require('./package.json');
|
||||
|
||||
/**
|
||||
* The global map of Android system types
|
||||
* @typedef {Map<string, CEIType>} AndroidLibrary
|
||||
@@ -131,6 +135,7 @@ connection.onInitialize((params) => {
|
||||
}
|
||||
|
||||
Settings.set(startupOpts.initialSettings);
|
||||
analytics.init(undefined, startupOpts.mpuid, uuidv4(), package_json, { vscode_version: startupOpts.vscodeVersion });
|
||||
|
||||
loadCodeCompletionLibrary(startupOpts.extensionPath, Settings.codeCompletionLibraries);
|
||||
|
||||
@@ -215,6 +220,14 @@ connection.onDidChangeConfiguration(async (change) => {
|
||||
|
||||
Settings.set(newSettings);
|
||||
|
||||
if (Settings.updateCount > 2) {
|
||||
analytics.event('ls-settings-changed', {
|
||||
appSourceRoot: Settings.appSourceRoot,
|
||||
libs: Settings.codeCompletionLibraries,
|
||||
trace: Settings.trace,
|
||||
})
|
||||
}
|
||||
|
||||
const new_ccl = [...new Set(Settings.codeCompletionLibraries)].sort();
|
||||
if (new_ccl.length !== prev_ccl.length || new_ccl.find((lib,idx) => lib !== prev_ccl[idx])) {
|
||||
// code-completion libraries have changed - reload the android library
|
||||
|
||||
Reference in New Issue
Block a user