coc.nvim
Version:
LSP based intellisense engine for neovim & vim8.
334 lines • 13.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const os_1 = tslib_1.__importDefault(require("os"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const vscode_uri_1 = require("vscode-uri");
const types_1 = require("../types");
const object_1 = require("../util/object");
const util_1 = require("../util");
const configuration_1 = require("./configuration");
const model_1 = require("./model");
const util_2 = require("./util");
const is_1 = require("../util/is");
const fs_2 = require("../util/fs");
const logger = require('../util/logger')('configurations');
function lookUp(tree, key) {
if (key) {
if (tree && tree.hasOwnProperty(key))
return tree[key];
const parts = key.split('.');
let node = tree;
for (let i = 0; node && i < parts.length; i++) {
node = node[parts[i]];
}
return node;
}
return tree;
}
class Configurations {
constructor(userConfigFile, _proxy) {
this.userConfigFile = userConfigFile;
this._proxy = _proxy;
this._errorItems = [];
this._folderConfigurations = new Map();
this._onError = new vscode_languageserver_protocol_1.Emitter();
this._onChange = new vscode_languageserver_protocol_1.Emitter();
this.disposables = [];
this.onError = this._onError.event;
this.onDidChange = this._onChange.event;
let user = this.parseContentFromFile(userConfigFile);
let data = {
defaults: util_2.loadDefaultConfigurations(),
user,
workspace: { contents: {} }
};
this._configuration = Configurations.parse(data);
this.watchFile(userConfigFile, types_1.ConfigurationTarget.User);
}
parseContentFromFile(filepath) {
if (!filepath)
return { contents: {} };
let uri = vscode_uri_1.URI.file(filepath).toString();
this._errorItems = this._errorItems.filter(o => o.location.uri != uri);
let res = util_2.parseContentFromFile(filepath, errors => {
this._errorItems.push(...errors);
});
this._onError.fire(this._errorItems);
return res;
}
get errorItems() {
return this._errorItems;
}
get foldConfigurations() {
return this._folderConfigurations;
}
// used for extensions, no change event fired
extendsDefaults(props) {
let { defaults } = this._configuration;
let { contents } = defaults;
contents = object_1.deepClone(contents);
Object.keys(props).forEach(key => {
util_2.addToValueTree(contents, key, props[key], msg => {
logger.error(msg); // tslint:disable-line
});
});
let data = {
defaults: { contents },
user: this._configuration.user,
workspace: this._configuration.workspace
};
this._configuration = Configurations.parse(data);
}
// change user configuration, without change file
updateUserConfig(props) {
if (!props || Object.keys(props).length == 0)
return;
let { user } = this._configuration;
let model = user.clone();
Object.keys(props).forEach(key => {
let val = props[key];
if (val === undefined) {
model.removeValue(key);
}
else if (is_1.objectLiteral(val)) {
for (let k of Object.keys(val)) {
model.setValue(`${key}.${k}`, val[k]);
}
}
else {
model.setValue(key, val);
}
});
this.changeConfiguration(types_1.ConfigurationTarget.User, model);
}
get defaults() {
return this._configuration.defaults;
}
get user() {
return this._configuration.user;
}
get workspace() {
return this._configuration.workspace;
}
addFolderFile(filepath) {
let { _folderConfigurations } = this;
if (_folderConfigurations.has(filepath))
return;
if (path_1.default.resolve(filepath, '../..') == os_1.default.homedir())
return;
let model = this.parseContentFromFile(filepath);
this.watchFile(filepath, types_1.ConfigurationTarget.Workspace);
this.changeConfiguration(types_1.ConfigurationTarget.Workspace, model, filepath);
}
watchFile(filepath, target) {
if (!fs_1.default.existsSync(filepath))
return;
if (global.hasOwnProperty('__TEST__'))
return;
let disposable = util_1.watchFile(filepath, () => {
let model = this.parseContentFromFile(filepath);
this.changeConfiguration(target, model, filepath);
});
this.disposables.push(disposable);
}
// create new configuration and fire change event
changeConfiguration(target, model, configFile) {
let { defaults, user, workspace } = this._configuration;
let { workspaceConfigFile } = this;
let data = {
defaults: target == types_1.ConfigurationTarget.Global ? model : defaults,
user: target == types_1.ConfigurationTarget.User ? model : user,
workspace: target == types_1.ConfigurationTarget.Workspace ? model : workspace,
};
let configuration = Configurations.parse(data);
let changed = util_2.getChangedKeys(this._configuration.getValue(), configuration.getValue());
if (target == types_1.ConfigurationTarget.Workspace && configFile) {
this._folderConfigurations.set(configFile, new model_1.ConfigurationModel(model.contents));
this.workspaceConfigFile = configFile;
}
if (changed.length == 0)
return;
this._configuration = configuration;
this._onChange.fire({
affectsConfiguration: (section, resource) => {
if (!resource || target != types_1.ConfigurationTarget.Workspace)
return changed.indexOf(section) !== -1;
let u = vscode_uri_1.URI.parse(resource);
if (u.scheme !== 'file')
return changed.indexOf(section) !== -1;
let filepath = u.fsPath;
let preRoot = workspaceConfigFile ? path_1.default.resolve(workspaceConfigFile, '../..') : '';
if (configFile && !fs_2.isParentFolder(preRoot, filepath) && !fs_2.isParentFolder(path_1.default.resolve(configFile, '../..'), filepath)) {
return false;
}
return changed.indexOf(section) !== -1;
}
});
}
setFolderConfiguration(uri) {
let u = vscode_uri_1.URI.parse(uri);
if (u.scheme != 'file')
return;
let filepath = u.fsPath;
for (let [configFile, model] of this.foldConfigurations) {
let root = path_1.default.resolve(configFile, '../..');
if (fs_2.isParentFolder(root, filepath) && this.workspaceConfigFile != configFile) {
this.changeConfiguration(types_1.ConfigurationTarget.Workspace, model, configFile);
break;
}
}
}
hasFolderConfiguration(filepath) {
let { folders } = this;
return folders.findIndex(f => fs_2.isParentFolder(f, filepath)) !== -1;
}
getConfigFile(target) {
if (target == types_1.ConfigurationTarget.Global)
return null;
if (target == types_1.ConfigurationTarget.User)
return this.userConfigFile;
return this.workspaceConfigFile;
}
get folders() {
let res = [];
let { _folderConfigurations } = this;
for (let folder of _folderConfigurations.keys()) {
res.push(path_1.default.resolve(folder, '../..'));
}
return res;
}
get configuration() {
return this._configuration;
}
/**
* getConfiguration
*
* @public
* @param {string} section
* @returns {WorkspaceConfiguration}
*/
getConfiguration(section, resource) {
let configuration;
if (resource) {
let { defaults, user } = this._configuration;
configuration = new configuration_1.Configuration(defaults, user, this.getFolderConfiguration(resource));
}
else {
configuration = this._configuration;
}
const config = Object.freeze(lookUp(configuration.getValue(null), section));
const result = {
has(key) {
return typeof lookUp(config, key) !== 'undefined';
},
get: (key, defaultValue) => {
let result = lookUp(config, key);
if (result == null)
return defaultValue;
return result;
},
update: (key, value, isUser = false) => {
let s = section ? `${section}.${key}` : key;
let target = isUser ? types_1.ConfigurationTarget.User : types_1.ConfigurationTarget.Workspace;
let model = target == types_1.ConfigurationTarget.User ? this.user.clone() : this.workspace.clone();
if (value == undefined) {
model.removeValue(s);
}
else {
model.setValue(s, value);
}
if (!this.workspaceConfigFile && this._proxy) {
let file = this.workspaceConfigFile = this._proxy.workspaceConfigFile;
if (!fs_1.default.existsSync(file)) {
let folder = path_1.default.dirname(file);
if (!fs_1.default.existsSync(folder))
fs_1.default.mkdirSync(folder);
fs_1.default.writeFileSync(file, '{}', { encoding: 'utf8' });
}
}
this.changeConfiguration(target, model, target == types_1.ConfigurationTarget.Workspace ? this.workspaceConfigFile : this.userConfigFile);
if (this._proxy && !global.hasOwnProperty('__TEST__')) {
if (value == undefined) {
this._proxy.$removeConfigurationOption(target, s);
}
else {
this._proxy.$updateConfigurationOption(target, s, value);
}
}
},
inspect: (key) => {
key = section ? `${section}.${key}` : key;
const config = this._configuration.inspect(key);
if (config) {
return {
key,
defaultValue: config.default,
globalValue: config.user,
workspaceValue: config.workspace,
};
}
return undefined;
}
};
Object.defineProperty(result, 'has', {
enumerable: false
});
Object.defineProperty(result, 'get', {
enumerable: false
});
Object.defineProperty(result, 'update', {
enumerable: false
});
Object.defineProperty(result, 'inspect', {
enumerable: false
});
if (typeof config === 'object') {
object_1.mixin(result, config, false);
}
return object_1.deepFreeze(result);
}
getFolderConfiguration(uri) {
let u = vscode_uri_1.URI.parse(uri);
if (u.scheme != 'file')
return new model_1.ConfigurationModel();
let filepath = u.fsPath;
for (let [configFile, model] of this.foldConfigurations) {
let root = path_1.default.resolve(configFile, '../..');
if (fs_2.isParentFolder(root, filepath))
return model;
}
return new model_1.ConfigurationModel();
}
checkFolderConfiguration(uri) {
let u = vscode_uri_1.URI.parse(uri);
if (u.scheme != 'file')
return;
let rootPath = path_1.default.dirname(u.fsPath);
if (!this.hasFolderConfiguration(rootPath)) {
let folder = fs_2.findUp('.vim', rootPath);
if (folder && folder != os_1.default.homedir()) {
let file = path_1.default.join(folder, 'coc-settings.json');
if (fs_1.default.existsSync(file)) {
this.addFolderFile(file);
}
}
}
else {
this.setFolderConfiguration(uri);
}
}
static parse(data) {
const defaultConfiguration = new model_1.ConfigurationModel(data.defaults.contents);
const userConfiguration = new model_1.ConfigurationModel(data.user.contents);
const workspaceConfiguration = new model_1.ConfigurationModel(data.workspace.contents);
return new configuration_1.Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, new model_1.ConfigurationModel());
}
dispose() {
util_1.disposeAll(this.disposables);
}
}
exports.default = Configurations;
//# sourceMappingURL=index.js.map