set configurable trace logging and update section names

This commit is contained in:
Dave Holoway
2020-06-28 21:05:08 +01:00
parent 18049ea08c
commit bb1a326768
6 changed files with 104 additions and 78 deletions

View File

@@ -18,6 +18,7 @@ const { Label, Local, MethodDeclarations, ResolvedIdent, ResolveInfo } = require
const { resolveImports, resolveSingleImport } = require('../java/import-resolver'); const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
const { checkAssignment, getTypeInheritanceList } = require('./expression-resolver'); const { checkAssignment, getTypeInheritanceList } = require('./expression-resolver');
const { checkStatementBlock } = require('./statement-validater'); const { checkStatementBlock } = require('./statement-validater');
const { time, timeEnd } = require('../logging');
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression"); const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression"); const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression");
@@ -199,10 +200,6 @@ function extractSourceTypes(tokens, typemap) {
*/ */
function parse(docs, cached_units, typemap) { function parse(docs, cached_units, typemap) {
const timers = new Set();
const time = name => (timers.add(name), console.time(name));
const timeEnd = name => (timers.delete(name), console.timeEnd(name));
time('tokenize'); time('tokenize');
const sources = docs.reduce((arr, doc) => { const sources = docs.reduce((arr, doc) => {
try { try {

View File

@@ -31,8 +31,6 @@ function parseMethodBodies(unit, typemap) {
* @returns {import('./parsetypes/parse-problem')[]} * @returns {import('./parsetypes/parse-problem')[]}
*/ */
function validate(unit, androidLibrary) { function validate(unit, androidLibrary) {
console.time('validation');
let probs = []; let probs = [];
const module_validaters = [ const module_validaters = [
@@ -54,7 +52,6 @@ function validate(unit, androidLibrary) {
module_validaters.map(v => v(unit.types, unit)), module_validaters.map(v => v(unit.types, unit)),
...probs, ...probs,
]; ];
console.timeEnd('validation');
function flatten(arr) { function flatten(arr) {
let res = arr; let res = arr;

43
langserver/logging.js Normal file
View File

@@ -0,0 +1,43 @@
const { Settings } = require('./settings');
const earlyTraceBuffer = [];
/**
* @param {string} s
*/
function trace(s) {
if (Settings.updateCount > 0 && !Settings.trace) {
return;
}
const msg = `${Date.now()}: ${s}`;
// before we've retrieved the trace setting, buffer the messages
if (Settings.updateCount === 0) {
earlyTraceBuffer.push(msg);
return;
}
if (earlyTraceBuffer.length) {
earlyTraceBuffer.splice(0, earlyTraceBuffer.length).forEach(msg => console.log(msg));
}
console.log(msg);
}
function info(msg) {
console.log(msg);
}
function time(label) {
if (Settings.trace) {
console.time(label);
}
}
function timeEnd(label) {
if (Settings.trace) {
console.timeEnd(label);
}
}
exports.info = info;
exports.trace = trace;
exports.time = time;
exports.timeEnd = timeEnd;

View File

@@ -4,30 +4,26 @@ const os = require('os');
const { const {
createConnection, createConnection,
TextDocuments, TextDocuments,
//TextDocument,
Diagnostic, Diagnostic,
DiagnosticSeverity,
ProposedFeatures, ProposedFeatures,
//InitializeParams,
DidChangeConfigurationNotification, DidChangeConfigurationNotification,
CompletionItem, CompletionItem,
CompletionItemKind, CompletionItemKind,
TextDocumentSyncKind, TextDocumentSyncKind,
Position,
//TextDocumentPositionParams
} = require('vscode-languageserver'); } = require('vscode-languageserver');
const { TextDocument } = require('vscode-languageserver-textdocument'); const { TextDocument } = require('vscode-languageserver-textdocument');
const { Settings } = require('./settings');
const { loadAndroidSystemLibrary } = require('./java/java-libraries'); const { loadAndroidSystemLibrary } = require('./java/java-libraries');
const { JavaType, CEIType, ArrayType, PrimitiveType, Method } = require('java-mti'); 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');
const { SourceUnit } = require('./java/source-types'); const { SourceUnit } = require('./java/source-types');
const { validate, parseMethodBodies } = require('./java/validater'); const { parseMethodBodies } = require('./java/validater');
const { getTypeInheritanceList } = require('./java/expression-resolver'); const { getTypeInheritanceList } = require('./java/expression-resolver');
const { Settings } = require('./settings');
const { trace, info, time, timeEnd } = require('./logging');
/** /**
* @typedef {Map<string, CEIType>} AndroidLibrary * @typedef {Map<string, CEIType>} AndroidLibrary
@@ -76,13 +72,6 @@ function positionAt(index, content) {
} }
} }
/**
* @param {string} s
*/
function trace(s) {
console.log(`${Date.now()}: ${s}`);
}
class JavaDocInfo { class JavaDocInfo {
/** /**
* @param {string} uri * @param {string} uri
@@ -226,7 +215,7 @@ function reparse(uris, opts) {
} }
if (method_body_uris.length) { if (method_body_uris.length) {
console.time('parse-methods'); time('parse-methods');
method_body_uris.forEach(uri => { method_body_uris.forEach(uri => {
const doc = liveParsers.get(uri); const doc = liveParsers.get(uri);
if (!doc || !doc.parsed) { if (!doc || !doc.parsed) {
@@ -234,7 +223,7 @@ function reparse(uris, opts) {
} }
parseMethodBodies(doc.parsed.unit, typemap); parseMethodBodies(doc.parsed.unit, typemap);
}) })
console.timeEnd('parse-methods'); timeEnd('parse-methods');
} }
} }
@@ -335,9 +324,12 @@ connection.onInitialize((params) => {
connection.onInitialized(async () => { connection.onInitialized(async () => {
if (hasConfigurationCapability) { if (hasConfigurationCapability) {
// Register for all configuration changes. // Register for all configuration changes.
connection.client.register(DidChangeConfigurationNotification.type, undefined); connection.client.register(
DidChangeConfigurationNotification.type, {
section: 'android-dev-ext',
});
const initialSettings = await connection.workspace.getConfiguration({ const initialSettings = await connection.workspace.getConfiguration({
section: Settings.ID, section: "android-dev-ext"
}); });
Settings.set(initialSettings); Settings.set(initialSettings);
} }
@@ -348,7 +340,7 @@ connection.onInitialized(async () => {
}); });
} }
const src_folder = await getAppRootFolder(); const src_folder = await getAppSourceRootFolder();
if (src_folder) { if (src_folder) {
await rescanSourceFolders(src_folder); await rescanSourceFolders(src_folder);
reparse([...liveParsers.keys()], { includeMethods: false, first_parse: true }); reparse([...liveParsers.keys()], { includeMethods: false, first_parse: true });
@@ -367,8 +359,8 @@ async function rescanSourceFolders(src_folder) {
return; return;
} }
// when the appRoot config value changes and we rescan the folder, we need // when the appSourceRoot config value changes and we rescan the folder, we need
// to delete any parsers that were from the old appRoot // to delete any parsers that were from the old appSourceRoot
const unused_keys = new Set(liveParsers.keys()); const unused_keys = new Set(liveParsers.keys());
const files = await loadWorkingFileList(src_folder); const files = await loadWorkingFileList(src_folder);
@@ -398,10 +390,10 @@ async function rescanSourceFolders(src_folder) {
} }
/** /**
* Attempts to locate the app root folder using workspace folders and the appRoot setting * Attempts to locate the app root folder using workspace folders and the appSourceRoot setting
* @returns Absolute path to app root folder or null * @returns Absolute path to app root folder or null
*/ */
async function getAppRootFolder() { async function getAppSourceRootFolder() {
/** @type {string} */ /** @type {string} */
let src_folder = null; let src_folder = null;
@@ -412,7 +404,7 @@ async function getAppRootFolder() {
} }
folders.find(folder => { folders.find(folder => {
const main_folder = path.join(folder.uri.replace(/^\w+:\/\//, ''), Settings.appRoot); const main_folder = path.join(folder.uri.replace(/^\w+:\/\//, ''), Settings.appSourceRoot);
try { try {
if (fs.statSync(main_folder).isDirectory()) { if (fs.statSync(main_folder).isDirectory()) {
src_folder = main_folder; src_folder = main_folder;
@@ -425,7 +417,7 @@ async function getAppRootFolder() {
console.log([ console.log([
`Failed to find source root from workspace folders:`, `Failed to find source root from workspace folders:`,
...folders.map(f => ` - ${f.uri}`), ...folders.map(f => ` - ${f.uri}`),
'Configure the Android App Root value in your workspace settings to point to your source folder containing AndroidManifest.xml', 'Configure the Android App Source Root value in your workspace settings to point to your source folder containing AndroidManifest.xml',
].join(os.EOL)); ].join(os.EOL));
} }
@@ -438,9 +430,9 @@ async function loadWorkingFileList(src_folder) {
} }
trace(`Using src root folder: ${src_folder}. Searching for Android project source files...`); trace(`Using src root folder: ${src_folder}. Searching for Android project source files...`);
console.time('source file search') time('source file search')
const files = scanSourceFiles(src_folder); const files = scanSourceFiles(src_folder);
console.timeEnd('source file search'); timeEnd('source file search');
if (!files.find(file => /^androidmanifest.xml$/i.test(file.relfpn))) { if (!files.find(file => /^androidmanifest.xml$/i.test(file.relfpn))) {
console.log(`Warning: No AndroidManifest.xml found in app root folder. Check the Android App Root value in your workspace settings.`) console.log(`Warning: No AndroidManifest.xml found in app root folder. Check the Android App Root value in your workspace settings.`)
@@ -492,18 +484,21 @@ async function loadWorkingFileList(src_folder) {
} }
connection.onDidChangeConfiguration(async (change) => { connection.onDidChangeConfiguration(async (change) => {
trace(`onDidChangeConfiguration`); trace(`onDidChangeConfiguration: ${JSON.stringify(change)}`);
if (change && change.settings && change.settings[Settings.ID]) { const old_app_root = Settings.appSourceRoot;
const old_app_root = Settings.appRoot; const newSettings = await connection.workspace.getConfiguration({
Settings.onChange(change.settings[Settings.ID]); section: "android-dev-ext"
if (old_app_root !== Settings.appRoot) { });
const src_folder = await getAppRootFolder();
Settings.set(newSettings);
if (old_app_root !== Settings.appSourceRoot) {
const src_folder = await getAppSourceRootFolder();
if (src_folder) { if (src_folder) {
rescanSourceFolders(src_folder); rescanSourceFolders(src_folder);
reparse([...liveParsers.keys()]); reparse([...liveParsers.keys()]);
} }
} }
}
}) })
documents.onDidClose((e) => { documents.onDidClose((e) => {
@@ -523,7 +518,7 @@ documents.onDidChangeContent((change) => {
*/ */
async function validateTextDocument(textDocument) { async function validateTextDocument(textDocument) {
if (androidLibrary instanceof Promise) { if (androidLibrary instanceof Promise) {
trace('Waiting for Android Library load'); trace('waiting for Android Library load to complete');
androidLibrary = await androidLibrary; androidLibrary = await androidLibrary;
} }
/** @type {ParseProblem[]} */ /** @type {ParseProblem[]} */

View File

@@ -1,6 +1,7 @@
const defaultSettings = { const defaultSettings = {
appRoot: 'app/src/main' appSourceRoot: 'app/src/main',
trace: false,
} }
class AndroidProjectSettings { class AndroidProjectSettings {
@@ -8,24 +9,22 @@ const defaultSettings = {
* The root of the app source folder. * The root of the app source folder.
* This folder should contain AndroidManifest.xml as well as the asets, res, etc folders * This folder should contain AndroidManifest.xml as well as the asets, res, etc folders
*/ */
appRoot = defaultSettings.appRoot; appSourceRoot = defaultSettings.appSourceRoot;
/** /**
* The identifier for the language server settings * True if we log details
*/ */
ID = 'androidJavaLanguageServer'; trace = defaultSettings.trace;
updateCount = 0;
static Instance = new AndroidProjectSettings(); static Instance = new AndroidProjectSettings();
/**
* Called when the user edits the settings
* @param {*} values
*/
onChange(values) {
this.set(values);
}
set(values) { set(values) {
if (!values || typeof values !== 'object') {
return;
}
this.updateCount += 1;
console.log(`settings set: ${JSON.stringify(values)}`); console.log(`settings set: ${JSON.stringify(values)}`);
for (let key in defaultSettings) { for (let key in defaultSettings) {
if (Object.prototype.hasOwnProperty.call(values, key)) { if (Object.prototype.hasOwnProperty.call(values, key)) {
@@ -35,20 +34,4 @@ const defaultSettings = {
} }
} }
// function getDocumentSettings(resource) {
// if (!hasConfigurationCapability) {
// return Promise.resolve(projectSettings);
// }
// let result = documentSettings.get(resource);
// if (!result) {
// result = connection.workspace.getConfiguration({
// scopeUri: resource,
// section: 'androidJavaLanguageServer',
// });
// documentSettings.set(resource, result);
// }
// return result;
// }
exports.Settings = AndroidProjectSettings.Instance; exports.Settings = AndroidProjectSettings.Instance;

View File

@@ -31,13 +31,24 @@
"contributes": { "contributes": {
"configuration": { "configuration": {
"type": "object", "type": "object",
"title": "Java (Android)", "title": "Android",
"properties": { "properties": {
"androidJavaLanguageServer.maxNumberOfProblems": { "android-dev-ext.appSourceRoot": {
"scope": "resource", "scope": "resource",
"type": "number", "type": "string",
"default": 100, "default": "app/src/main",
"description": "Controls the maximum number of problems produced by the server." "description": "Relative path to the app source files. This folder should contain AndroidManifest.xml."
},
"android-dev-ext.subscriptionKey": {
"scope": "application",
"type": "string",
"default": ""
},
"android-dev-ext.trace": {
"scope": "resource",
"type": "boolean",
"default": false,
"description": "Enable detailed trace logging."
} }
} }
}, },