mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +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
|
// Import the module and reference it with the alias vscode in your code below
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const vscode = require('vscode');
|
const vscode = require('vscode');
|
||||||
|
const analytics = require('./langserver/analytics');
|
||||||
|
const package_json = require('./package.json');
|
||||||
const { LanguageClient, TransportKind, } = require('vscode-languageclient');
|
const { LanguageClient, TransportKind, } = require('vscode-languageclient');
|
||||||
const { AndroidContentProvider } = require('./src/contentprovider');
|
const { AndroidContentProvider } = require('./src/contentprovider');
|
||||||
const { openLogcatWindow } = require('./src/logcat');
|
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 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
|
// Options to control the language client
|
||||||
/** @type {import('vscode-languageclient').LanguageClientOptions} */
|
/** @type {import('vscode-languageclient').LanguageClientOptions} */
|
||||||
let clientOptions = {
|
let clientOptions = {
|
||||||
@@ -53,8 +57,11 @@ async function createLanguageClient(context) {
|
|||||||
initializationOptions: {
|
initializationOptions: {
|
||||||
// extensionPath points to the root of the extension (the folder where this file is)
|
// extensionPath points to the root of the extension (the folder where this file is)
|
||||||
extensionPath: context.extensionPath,
|
extensionPath: context.extensionPath,
|
||||||
|
mpuid: mpids.uid,
|
||||||
|
mpsid: mpids.sid,
|
||||||
initialSettings: config,
|
initialSettings: config,
|
||||||
sourceFiles,
|
sourceFiles,
|
||||||
|
vscodeVersion: vscode.version,
|
||||||
workspaceFolders: (vscode.workspace.workspaceFolders || []).map(z => z.uri.toString()),
|
workspaceFolders: (vscode.workspace.workspaceFolders || []).map(z => z.uri.toString()),
|
||||||
},
|
},
|
||||||
synchronize: {
|
synchronize: {
|
||||||
@@ -107,6 +114,9 @@ function activate(context) {
|
|||||||
/* Only the logcat stuff is configured here. The debugger is launched from src/debugMain.js */
|
/* Only the logcat stuff is configured here. The debugger is launched from src/debugMain.js */
|
||||||
AndroidContentProvider.register(context, vscode.workspace);
|
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 => {
|
createLanguageClient(context).then(client => {
|
||||||
languageClient = client;
|
languageClient = client;
|
||||||
refreshLanguageServerEnabledState();
|
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 { indexAt } = require('./document');
|
||||||
const { formatDoc } = require('./doc-formatter');
|
const { formatDoc } = require('./doc-formatter');
|
||||||
const { trace } = require('./logging');
|
const { trace } = require('./logging');
|
||||||
|
const { event } = require('./analytics');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Case-insensitive sort routines
|
* Case-insensitive sort routines
|
||||||
@@ -276,6 +277,8 @@ let defaultCompletionTypes = null;
|
|||||||
/** @type {Map<string,CEIType>} */
|
/** @type {Map<string,CEIType>} */
|
||||||
let lastCompletionTypeMap = null;
|
let lastCompletionTypeMap = null;
|
||||||
|
|
||||||
|
let completionRequestCount = 0;
|
||||||
|
|
||||||
function initDefaultCompletionTypes(lib) {
|
function initDefaultCompletionTypes(lib) {
|
||||||
defaultCompletionTypes = {
|
defaultCompletionTypes = {
|
||||||
instances: 'this super'.split(' ').map(t => ({
|
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;
|
let parsed = docinfo.parsed;
|
||||||
|
|
||||||
// save the typemap associated with this parsed state - we use this when resolving
|
// save the typemap associated with this parsed state - we use this when resolving
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { CEIType, loadJavaLibraryCacheFile } = require('java-mti');
|
const { CEIType, loadJavaLibraryCacheFile } = require('java-mti');
|
||||||
|
const analytics = require('../analytics');
|
||||||
const { trace, time, timeEnd } = require('../logging');
|
const { trace, time, timeEnd } = require('../logging');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,6 +10,7 @@ const { trace, time, timeEnd } = require('../logging');
|
|||||||
* @returns {Promise<Map<string,CEIType>>}
|
* @returns {Promise<Map<string,CEIType>>}
|
||||||
*/
|
*/
|
||||||
async function loadAndroidSystemLibrary(extensionPath, additional_libs) {
|
async function loadAndroidSystemLibrary(extensionPath, additional_libs) {
|
||||||
|
analytics.time('android-library-load');
|
||||||
time('android-library-load');
|
time('android-library-load');
|
||||||
let library;
|
let library;
|
||||||
try {
|
try {
|
||||||
@@ -25,6 +27,7 @@ async function loadAndroidSystemLibrary(extensionPath, additional_libs) {
|
|||||||
library = typemap;
|
library = typemap;
|
||||||
} finally {
|
} finally {
|
||||||
timeEnd('android-library-load');
|
timeEnd('android-library-load');
|
||||||
|
analytics.timeEnd('android-library-load', 'ms', { libs: additional_libs, typecount: library ? library.size : 0 });
|
||||||
}
|
}
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ const { Method } = require('java-mti');
|
|||||||
const { indexAt } = require('./document');
|
const { indexAt } = require('./document');
|
||||||
const { formatDoc } = require('./doc-formatter');
|
const { formatDoc } = require('./doc-formatter');
|
||||||
const { trace } = require('./logging');
|
const { trace } = require('./logging');
|
||||||
|
const { event } = require('./analytics');
|
||||||
|
|
||||||
|
let methodsigRequestCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve method signature information
|
* Retrieve method signature information
|
||||||
@@ -36,6 +39,11 @@ async function getSignatureHelp(request, liveParsers) {
|
|||||||
// wait for any active edits to complete
|
// wait for any active edits to complete
|
||||||
await docinfo.reparseWaiter;
|
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
|
// locate the token at the requested position
|
||||||
const index = indexAt(request.position, docinfo.content);
|
const index = indexAt(request.position, docinfo.content);
|
||||||
const token = docinfo.parsed.unit.getTokenAt(index);
|
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",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
},
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q=="
|
||||||
|
},
|
||||||
"vscode-jsonrpc": {
|
"vscode-jsonrpc": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"java-mti": "adelphes/java-mti#d0e1e45",
|
"java-mti": "adelphes/java-mti#d0e1e45",
|
||||||
"mixpanel": "^0.11.0",
|
"mixpanel": "^0.11.0",
|
||||||
|
"uuid": "^8.2.0",
|
||||||
"vscode-languageserver": "6.1.1",
|
"vscode-languageserver": "6.1.1",
|
||||||
"vscode-languageserver-textdocument": "1.0.1",
|
"vscode-languageserver-textdocument": "1.0.1",
|
||||||
"vscode-uri": "2.1.2"
|
"vscode-uri": "2.1.2"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ const { clearDefaultCompletionEntries, getCompletionItems, resolveCompletionItem
|
|||||||
const { getSignatureHelp } = require('./method-signatures');
|
const { getSignatureHelp } = require('./method-signatures');
|
||||||
const { FileURIMap, JavaDocInfo, indexAt, reparse } = require('./document');
|
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
|
* The global map of Android system types
|
||||||
* @typedef {Map<string, CEIType>} AndroidLibrary
|
* @typedef {Map<string, CEIType>} AndroidLibrary
|
||||||
@@ -131,6 +135,7 @@ connection.onInitialize((params) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Settings.set(startupOpts.initialSettings);
|
Settings.set(startupOpts.initialSettings);
|
||||||
|
analytics.init(undefined, startupOpts.mpuid, uuidv4(), package_json, { vscode_version: startupOpts.vscodeVersion });
|
||||||
|
|
||||||
loadCodeCompletionLibrary(startupOpts.extensionPath, Settings.codeCompletionLibraries);
|
loadCodeCompletionLibrary(startupOpts.extensionPath, Settings.codeCompletionLibraries);
|
||||||
|
|
||||||
@@ -215,6 +220,14 @@ connection.onDidChangeConfiguration(async (change) => {
|
|||||||
|
|
||||||
Settings.set(newSettings);
|
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();
|
const new_ccl = [...new Set(Settings.codeCompletionLibraries)].sort();
|
||||||
if (new_ccl.length !== prev_ccl.length || new_ccl.find((lib,idx) => lib !== prev_ccl[idx])) {
|
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
|
// code-completion libraries have changed - reload the android library
|
||||||
|
|||||||
Reference in New Issue
Block a user