mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
cache decoded android library in globalStoragePath
This commit is contained in:
@@ -11,6 +11,9 @@ const { selectTargetDevice } = require('./src/utils/device');
|
|||||||
/** @type {LanguageClient} */
|
/** @type {LanguageClient} */
|
||||||
let client;
|
let client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {vscode.ExtensionContext} context
|
||||||
|
*/
|
||||||
function activateLanguageClient(context) {
|
function activateLanguageClient(context) {
|
||||||
// The server is implemented in node
|
// The server is implemented in node
|
||||||
let serverModule = context.asAbsolutePath(path.join('langserver', 'server.js'));
|
let serverModule = context.asAbsolutePath(path.join('langserver', 'server.js'));
|
||||||
@@ -38,10 +41,14 @@ function activateLanguageClient(context) {
|
|||||||
let clientOptions = {
|
let clientOptions = {
|
||||||
// Register the server for plain text documents
|
// Register the server for plain text documents
|
||||||
documentSelector: [{ scheme: 'file', language: 'java' }],
|
documentSelector: [{ scheme: 'file', language: 'java' }],
|
||||||
|
initializationOptions: {
|
||||||
|
// globalStoragePath is used to cache decoded jar files
|
||||||
|
globalStoragePath: context.globalStoragePath,
|
||||||
|
},
|
||||||
synchronize: {
|
synchronize: {
|
||||||
// Notify the server about file changes to '.java files contained in the workspace
|
// Notify the server about file changes to '.java files contained in the workspace
|
||||||
fileEvents: vscode.workspace.createFileSystemWatcher('**/.java')
|
fileEvents: vscode.workspace.createFileSystemWatcher('**/.java')
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the language client and start the client.
|
// Create the language client and start the client.
|
||||||
|
|||||||
174
langserver/java/java-libraries.js
Normal file
174
langserver/java/java-libraries.js
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { loadAndroidLibrary, CEIType } = require('java-mti');
|
||||||
|
|
||||||
|
const android_system_library_cache_filename = {
|
||||||
|
regex: /^sdk-platforms-android-(.+?)-(\d+)\.json$/,
|
||||||
|
/**
|
||||||
|
* @param {string} version
|
||||||
|
* @param {fs.Stats} stat
|
||||||
|
*/
|
||||||
|
build(version, stat) {
|
||||||
|
return `sdk-platforms-${version}-${Math.trunc(stat.mtime.getTime())}.json`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} cache_dir directory used to store decoded jar libraries
|
||||||
|
*/
|
||||||
|
function ensureFolderExists(cache_dir) {
|
||||||
|
if (!cache_dir) {
|
||||||
|
throw new Error('missing cache dir value');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const stat = fs.statSync(cache_dir);
|
||||||
|
if (!stat.isDirectory()) {
|
||||||
|
throw new Error(`cache dir '${cache_dir}' is not a directory`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
throw new Error(`cache dir '${cache_dir}' check failed: ${err.message}`);
|
||||||
|
}
|
||||||
|
fs.mkdirSync(cache_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} cache_dir
|
||||||
|
* @param {RegExp} filter
|
||||||
|
* @returns {fs.Dirent[]}
|
||||||
|
*/
|
||||||
|
function findLibraryFiles(cache_dir, filter) {
|
||||||
|
const files = fs.readdirSync(cache_dir, { withFileTypes: true });
|
||||||
|
const valid_files = files.filter(file => filter.test(file.name) && file.isFile());
|
||||||
|
return valid_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {fs.Dirent[]} files
|
||||||
|
*/
|
||||||
|
function chooseAndroidSystemLibrary(files) {
|
||||||
|
let chosen = {
|
||||||
|
api: 0,
|
||||||
|
/** @type {fs.Dirent} */
|
||||||
|
file: null,
|
||||||
|
};
|
||||||
|
files.forEach(file => {
|
||||||
|
const m = file.name.match(android_system_library_cache_filename.regex);
|
||||||
|
if (!m) return;
|
||||||
|
if (/^\d+$/.test(m[1])) {
|
||||||
|
const api = parseInt(m[1]);
|
||||||
|
if (api > chosen.api) {
|
||||||
|
chosen.api = api;
|
||||||
|
chosen.file = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return chosen.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findHighestAPISystemLibrary(android_sdk_platforms_root) {
|
||||||
|
let platform_folders = [], android_platform_jars = [];
|
||||||
|
try {
|
||||||
|
platform_folders = fs.readdirSync(android_sdk_platforms_root, { withFileTypes: true });
|
||||||
|
} catch {};
|
||||||
|
platform_folders.forEach(folder => {
|
||||||
|
if (!folder.isDirectory()) return;
|
||||||
|
// we assume stable SDK platform folders are named 'android-<api-level>'
|
||||||
|
if (!/^android-\d+$/.test(folder.name)) return;
|
||||||
|
// the platform folder must contain an android.jar file
|
||||||
|
let stat, filepath = path.join(android_sdk_platforms_root, folder.name, 'android.jar');
|
||||||
|
try { stat = fs.statSync(filepath) }
|
||||||
|
catch { return }
|
||||||
|
if (!stat.isFile()) return;
|
||||||
|
// add it to the list
|
||||||
|
android_platform_jars.push({
|
||||||
|
folder: folder.name,
|
||||||
|
api: parseInt(folder.name.split('-')[1], 10),
|
||||||
|
filepath,
|
||||||
|
stat,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (android_platform_jars.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// choose the folder with the highest API number
|
||||||
|
return android_platform_jars.sort((a,b) => b.api - a.api)[0].folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} cache_dir
|
||||||
|
* @param {string|':latest'} [version]
|
||||||
|
*/
|
||||||
|
async function buildAndroidSystemLibrary(cache_dir, version) {
|
||||||
|
const android_sdk_root = process.env['ANDROID_SDK'] || process.env['ANDROID_HOME'];
|
||||||
|
if (!android_sdk_root) {
|
||||||
|
throw new Error('Cannot locate Android SDK folder - ANDROID_SDK env variable is not defined');
|
||||||
|
}
|
||||||
|
const android_sdk_platforms_root = path.join(android_sdk_root, 'platforms');
|
||||||
|
|
||||||
|
if (!version) {
|
||||||
|
// choose the folder with the highest API number
|
||||||
|
version = findHighestAPISystemLibrary(android_sdk_platforms_root);
|
||||||
|
if (!version) {
|
||||||
|
throw new Error(`Cannot build Android library: No supported system libraries found in ${android_sdk_platforms_root}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stat, filepath = path.join(android_sdk_platforms_root, version, 'android.jar');
|
||||||
|
try { stat = fs.statSync(filepath) }
|
||||||
|
catch { }
|
||||||
|
if (!stat || !stat.isFile()) {
|
||||||
|
throw new Error(`Cannot build Android library: '${filepath}' is not a valid android system library file`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Building ${version} library cache for code completion support. This might take a few minutes...`);
|
||||||
|
const cache_filename = path.join(cache_dir, android_system_library_cache_filename.build(version, stat));
|
||||||
|
try {
|
||||||
|
const library = await loadAndroidLibrary(cache_filename, { api: version, sdk_root: android_sdk_root });
|
||||||
|
console.log(`${version} library cache built.`);
|
||||||
|
return library;
|
||||||
|
} catch(err) {
|
||||||
|
throw new Error(`Cannot build Android library: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} cache_dir directory used to store decoded jar libraries
|
||||||
|
* @returns {Promise<Map<string,CEIType>>}
|
||||||
|
*/
|
||||||
|
async function loadAndroidSystemLibrary(cache_dir) {
|
||||||
|
console.time('android-library-load');
|
||||||
|
// for (let x;;) {
|
||||||
|
// console.log('waiting');
|
||||||
|
// if (x) break;
|
||||||
|
// await new Promise(res => setTimeout(res, 1000));
|
||||||
|
// }
|
||||||
|
let library;
|
||||||
|
try {
|
||||||
|
ensureFolderExists(cache_dir);
|
||||||
|
const library_files = findLibraryFiles(cache_dir, android_system_library_cache_filename.regex);
|
||||||
|
if (!library_files.length) {
|
||||||
|
return buildAndroidSystemLibrary(cache_dir);
|
||||||
|
}
|
||||||
|
// search for the highest android API number in the list of cached files
|
||||||
|
const library_file = chooseAndroidSystemLibrary(library_files);
|
||||||
|
if (!library_file) {
|
||||||
|
return buildAndroidSystemLibrary(cache_dir);
|
||||||
|
}
|
||||||
|
// load the library
|
||||||
|
const library_path_name = path.join(cache_dir, library_file.name);
|
||||||
|
console.log(`Loading android system library: ${library_path_name}`);
|
||||||
|
library = await loadAndroidLibrary(library_path_name, null);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`android library load failed: ${err.message}`);
|
||||||
|
library = new Map();
|
||||||
|
}
|
||||||
|
console.timeEnd('android-library-load');
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.loadAndroidSystemLibrary = loadAndroidSystemLibrary;
|
||||||
@@ -18,7 +18,8 @@ const {
|
|||||||
|
|
||||||
const { TextDocument } = require('vscode-languageserver-textdocument');
|
const { TextDocument } = require('vscode-languageserver-textdocument');
|
||||||
|
|
||||||
const { loadAndroidLibrary, JavaType, CEIType, ArrayType, PrimitiveType, Method } = require('java-mti');
|
const { loadAndroidSystemLibrary } = require('./java/java-libraries');
|
||||||
|
const { JavaType, CEIType, ArrayType, PrimitiveType, Method } = require('java-mti');
|
||||||
|
|
||||||
const { ParseProblem } = require('./java/parser');
|
const { ParseProblem } = require('./java/parser');
|
||||||
const { parse } = require('./java/body-parser3');
|
const { parse } = require('./java/body-parser3');
|
||||||
@@ -289,14 +290,16 @@ let hasWorkspaceFolderCapability = false;
|
|||||||
let hasDiagnosticRelatedInformationCapability = false;
|
let hasDiagnosticRelatedInformationCapability = false;
|
||||||
|
|
||||||
connection.onInitialize((params) => {
|
connection.onInitialize((params) => {
|
||||||
console.time('android-library-load')
|
|
||||||
androidLibrary = loadAndroidLibrary('android-25').then(lib => {
|
// the storage path is passed to us by the client side of the extension
|
||||||
console.timeEnd('android-library-load')
|
const library_cache_path = (params.initializationOptions || {}).globalStoragePath;
|
||||||
return androidLibrary = lib;
|
trace(`library_cache_path: ${library_cache_path}`);
|
||||||
}, err => {
|
|
||||||
trace(`android library load failed: ${err.message}`);
|
// the android library is loaded asynchronously, with the global `androidLibrary` variable
|
||||||
return androidLibrary = new Map();
|
// set to the promise while it is loading.
|
||||||
});
|
androidLibrary = loadAndroidSystemLibrary(library_cache_path)
|
||||||
|
.then(library => androidLibrary = library);
|
||||||
|
|
||||||
let capabilities = params.capabilities;
|
let capabilities = params.capabilities;
|
||||||
|
|
||||||
// Does the client support the `workspace/configuration` request?
|
// Does the client support the `workspace/configuration` request?
|
||||||
|
|||||||
Reference in New Issue
Block a user