get setting from client

This commit is contained in:
全卓 2022-11-27 11:09:30 +08:00
parent 0c4012b71c
commit 1140368757
6 changed files with 163 additions and 64 deletions

View File

@ -77,6 +77,11 @@
{
"title": "CMake IntelliSence",
"properties": {
"cmakeIntelliSence.cmakePath": {
"type": "string",
"default": "cmake",
"description": "Set path to CMake executable"
},
"cmakeIntelliSence.loggingLevel": {
"type": "string",
"enum": [

55
server/src/cmakeInfo.ts Normal file
View File

@ -0,0 +1,55 @@
import { LSPAny } from "vscode-languageserver";
import { connection } from "./server";
import ExtensionSettings, { extSettings } from "./settings";
import * as cp from 'child_process';
import { promisify } from 'util';
type Modules = string[];
type Policies = string[];
type Variables = string[];
type Properties = string[];
class CMakeInfo {
public version: string;
public major: number;
public minor: number;
public patch: number;
public modules: string[];
public policies: string[];
public variables: string[];
public properties: string[];
public async init() {
[
[this.version, this.major, this.minor, this.patch],
[this.modules, this.policies, this.variables, this.properties]
] = await Promise.all([this.getCMakeVersion(), this.getBuiltinEntries()]);
}
private async getCMakeVersion(): Promise<[string, number, number, number]> {
const command = extSettings.cmakePath + " --version";
const { stdout, stderr } = await promisify(cp.exec)(command);
const regexp: RegExp = /(\d+)\.(\d+)\.(\d+)/;
const res = stdout.match(regexp);
return [
res[0],
parseInt(res[1]),
parseInt(res[2]),
parseInt(res[3])
];
}
private async getBuiltinEntries(): Promise<[Modules, Policies, Variables, Properties]> {
const command = extSettings.cmakePath + " --help-module-list --help-policy-list --help-variable-list --help-property-list";
const { stdout, stderr } = await promisify(cp.exec)(command);
const tmp = stdout.trim().split('\n\n\n');
return [
tmp[0].split('\n'),
tmp[1].split('\n'),
tmp[2].split('\n'),
tmp[3].split('\n'),
];
}
}
export const cmakeInfo = new CMakeInfo();

View File

@ -2,8 +2,9 @@ import { SemanticTokens, SemanticTokensBuilder } from "vscode-languageserver";
import { URI } from "vscode-uri";
import Token from "./parser/antlr4/Token";
import CMakeListener from "./parser/CMakeListener";
import { initParams, variables } from "./server";
import { initParams } from "./server";
import * as builtinCmds from './builtin-cmds.json';
import { cmakeInfo } from "./cmakeInfo";
export let tokenTypes = [
'type',
@ -105,7 +106,7 @@ export class SemanticListener extends CMakeListener {
}
private isVariable(token: string): boolean {
return variables.includes(token);
return cmakeInfo.variables.includes(token);
}
private getModifiers(modifiers: TokenModifiers[]): number {

View File

@ -1,13 +1,12 @@
import {
createConnection, HoverParams, InitializeParams, InitializeResult,
ProposedFeatures, SemanticTokensParams, SemanticTokensRequest, SignatureHelpParams, TextDocuments, TextDocumentSyncKind,
_RemoteWindow
createConnection, DidChangeConfigurationNotification, HoverParams, InitializeParams, InitializeResult,
LSPAny,
ProposedFeatures, SemanticTokensParams, SignatureHelpParams, TextDocuments, TextDocumentSyncKind
} from 'vscode-languageserver/node';
import {
CompletionItemKind, CompletionParams, DocumentFormattingParams,
DocumentSymbolParams, SignatureHelpTriggerKind, DefinitionParams,
WorkspaceFolder
CompletionItemKind, CompletionParams, DefinitionParams, DocumentFormattingParams,
DocumentSymbolParams, SignatureHelpTriggerKind
} from 'vscode-languageserver-protocol';
import { Range, TextDocument } from 'vscode-languageserver-textdocument';
import {
@ -15,18 +14,19 @@ import {
} from 'vscode-languageserver-types';
import { exec } from 'child_process';
import { existsSync } from 'fs';
import { URI, Utils } from 'vscode-uri';
import * as builtinCmds from './builtin-cmds.json';
import { SymbolListener } from './docSymbols';
import { FormatListener } from './format';
import antlr4 from './parser/antlr4/index.js';
import CMakeLexer from './parser/CMakeLexer.js';
import CMakeParser from './parser/CMakeParser.js';
import { SymbolListener } from './docSymbols';
import { Entries, getBuiltinEntries, getCMakeVersion, getFileContext } from './utils';
import { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
import { existsSync } from 'fs';
import { URI, Utils } from 'vscode-uri';
import path = require('path');
import { getTokenModifiers, getTokenTypes, SemanticListener, tokenBuilders } from './semanticTokens';
import { DefinationListener, incToBaseDir, refToDef, topScope } from './symbolTable/goToDefination';
import { getFileContext } from './utils';
import ExtensionSettings, { extSettings } from './settings';
import { cmakeInfo } from './cmakeInfo';
type Word = {
text: string,
@ -34,26 +34,21 @@ type Word = {
col: number
};
const entries: Entries = getBuiltinEntries();
export const modules = entries[0].split('\n');
export const policies = entries[1].split('\n');
export const variables = entries[2].split('\n');
export const properties = entries[3].split('\n');
let contentChanged = true;
export let initParams: InitializeParams;
// Craete a connection for the server, using Node's IPC as a transport.
// Create a connection for the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
const connection = createConnection(ProposedFeatures.all);
export const connection = createConnection(ProposedFeatures.all);
// Create a simple text document manager.
export const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
connection.onInitialize((params: InitializeParams) => {
connection.onInitialize(async (params: InitializeParams) => {
initParams = params;
const result: InitializeResult = {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
@ -83,11 +78,15 @@ connection.onInitialize((params: InitializeParams) => {
return result;
});
connection.onInitialized(() => {
connection.onInitialized(async () => {
console.log("Initialized");
connection.client.register(DidChangeConfigurationNotification.type, undefined);
await extSettings.getSettings();
await cmakeInfo.init();
});
connection.onHover((params: HoverParams) => {
connection.onHover(async (params: HoverParams) => {
const document: TextDocument = documents.get(params.textDocument.uri);
const word = getWordAtPosition(document, params.position).text;
if (word.length === 0) {
@ -110,7 +109,7 @@ connection.onHover((params: HoverParams) => {
let moduleArg = '';
if (modules.includes(word)) {
if (cmakeInfo.modules.includes(word)) {
const line = document.getText({
start: { line: params.position.line, character: 0 },
end: { line: params.position.line, character: Number.MAX_VALUE }
@ -118,11 +117,11 @@ connection.onHover((params: HoverParams) => {
if (line.trim().startsWith('include')) {
moduleArg = '--help-module ';
}
} else if (policies.includes(word)) {
} else if (cmakeInfo.policies.includes(word)) {
moduleArg = '--help-policy ';
} else if (variables.includes(word)) {
} else if (cmakeInfo.variables.includes(word)) {
moduleArg = '--help-variable ';
} else if (properties.includes(word)) {
} else if (cmakeInfo.properties.includes(word)) {
moduleArg = '--help-property ';
}
@ -153,10 +152,10 @@ connection.onCompletion(async (params: CompletionParams) => {
const results = await Promise.all([
getCommandProposals(word),
getProposals(word, CompletionItemKind.Module, modules),
getProposals(word, CompletionItemKind.Constant, policies),
getProposals(word, CompletionItemKind.Variable, variables),
getProposals(word, CompletionItemKind.Property, properties)
getProposals(word, CompletionItemKind.Module, cmakeInfo.modules),
getProposals(word, CompletionItemKind.Constant, cmakeInfo.policies),
getProposals(word, CompletionItemKind.Variable, cmakeInfo.variables),
getProposals(word, CompletionItemKind.Property, cmakeInfo.properties)
]);
return results.flat();
});
@ -327,6 +326,26 @@ connection.languages.semanticTokens.on(async (params: SemanticTokensParams) => {
return semanticListener.getSemanticTokens();
});
/**
* @param params This argument is null when configuration changed
*
* there are two different configuration models. A push model (the old) where
* the client pushed settings to the server. In this model the client takes a
* settings configuration which settings to push if they change. The new model
* is the pull model where the server pulls for settings. This model has the
* advantage that the pull can contain a scope (e.g. a resource). In this model
* the clients simply sends an empty change event to signal that the settings
* have changed and must be reread. The client can't send the changes in the event
* since the settings might be different for different resources.
*
* see the following two issues for detail
* https://github.com/microsoft/vscode/issues/54821
* https://github.com/microsoft/vscode-languageserver-node/issues/380
*/
connection.onDidChangeConfiguration(params => {
extSettings.getSettings();
});
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {

15
server/src/settings.ts Normal file
View File

@ -0,0 +1,15 @@
import { connection } from "./server";
export default class ExtensionSettings {
public loggingLevel: string;
public cmakePath: string;
public async getSettings() {
[this.cmakePath, this.loggingLevel] = await connection.workspace.getConfiguration([
{ section: 'cmakeIntelliSence.cmakePath' },
{ section: 'cmakeIntelliSence.loggingLevel' }
]);
}
}
export const extSettings: ExtensionSettings = new ExtensionSettings();

View File

@ -1,5 +1,5 @@
import * as cp from 'child_process';
import { documents } from './server';
import { documents} from './server';
import { existsSync } from 'fs';
import * as os from 'os';
@ -10,39 +10,43 @@ import CMakeLexer from "./parser/CMakeLexer";
import CMakeParser from "./parser/CMakeParser";
import InputStream from './parser/antlr4/InputStream';
import { URI, Utils } from 'vscode-uri';
import { cmakeInfo } from './cmakeInfo';
export type Entries = [string, string, string, string];
// export type Entries = [string, string, string, string];
export type CMakeVersion = {
version: string,
major: number,
minor: number,
patch: number
};
// export type CMakeVersion = {
// version: string,
// major: number,
// minor: number,
// patch: number
// };
export let cmakeVersion: CMakeVersion = getCMakeVersion();
// export let cmakeVersion: CMakeVersion = getCMakeVersion();
export function getBuiltinEntries(): Entries {
const args = ['cmake', '--help-module-list', '--help-policy-list',
'--help-variable-list', '--help-property-list'];
const cmd: string = args.join(' ');
// TODO: execute command async
const output = cp.execSync(cmd, { encoding: 'utf-8' });
return output.trim().split('\n\n\n') as Entries;
}
// export function getBuiltinEntries(): Entries {
// if (extSettings === undefined) {
// getConfiguration();
// }
// const args = [extSettings[ExtSettings.cmakePath], '--help-module-list', '--help-policy-list',
// '--help-variable-list', '--help-property-list'];
// const cmd: string = args.join(' ');
// // TODO: execute command async
// const output = cp.execSync(cmd, { encoding: 'utf-8' });
// return output.trim().split('\n\n\n') as Entries;
// }
export function getCMakeVersion(): CMakeVersion {
const args = ['cmake', '--version'];
const output: string = cp.execSync(args.join(' '), { encoding: 'utf-8' });
const regexp: RegExp = /(\d+)\.(\d+)\.(\d+)/;
const res = output.match(regexp);
return {
version: res[0],
major: parseInt(res[1]),
minor: parseInt(res[2]),
patch: parseInt(res[3])
};
}
// export function getCMakeVersion(): CMakeVersion {
// const args = [extSettings[ExtSettings.cmakePath], '--version'];
// const output: string = cp.execSync(args.join(' '), { encoding: 'utf-8' });
// const regexp: RegExp = /(\d+)\.(\d+)\.(\d+)/;
// const res = output.match(regexp);
// return {
// version: res[0],
// major: parseInt(res[1]),
// minor: parseInt(res[2]),
// patch: parseInt(res[3])
// };
// }
export function getFileContext(uri: URI) {
const document = documents.get(uri.toString());
@ -79,7 +83,7 @@ export function getIncludeFileUri(baseDir: URI, includeFileName: string): URI {
return null;
}
const moduleDir = 'cmake-' + cmakeVersion.major + '.' + cmakeVersion.minor;
const moduleDir = 'cmake-' + cmakeInfo.major + '.' + cmakeInfo.minor;
const resPath = path.join(cmakePath, '../..', 'share', moduleDir, 'Modules', includeFileName) + '.cmake';
if (existsSync(resPath)) {