feat(intellisense): add hover feature for intelliSense
This commit is contained in:
Родитель
8b15446cb1
Коммит
fc73d089c8
|
@ -26,12 +26,12 @@ export class Utility {
|
|||
const pattern = new RegExp(keys.join("|"), flag);
|
||||
return str.replace(pattern, (matched) => {
|
||||
const value: string | undefined = replacement.get(matched);
|
||||
return value ? value : matched;
|
||||
return value || matched;
|
||||
});
|
||||
}
|
||||
|
||||
public static async validateModelName(name: string, type: ModelType, folder?: string): Promise<string | undefined> {
|
||||
if (!name || name.trim() === "") {
|
||||
if (!name || name.trim() === Constants.EMPTY_STRING) {
|
||||
return `Name ${Constants.NOT_EMPTY_MSG}`;
|
||||
}
|
||||
if (!Constants.MODEL_NAME_REGEX.test(name)) {
|
||||
|
@ -48,7 +48,7 @@ export class Utility {
|
|||
}
|
||||
|
||||
public static validateNotEmpty(name: string, placeholder: string): string | undefined {
|
||||
if (!name || name.trim() === "") {
|
||||
if (!name || name.trim() === Constants.EMPTY_STRING) {
|
||||
return `${placeholder} ${Constants.NOT_EMPTY_MSG}`;
|
||||
}
|
||||
return undefined;
|
||||
|
|
|
@ -25,7 +25,7 @@ export class DeviceModelManager {
|
|||
}
|
||||
|
||||
public static generateModelFileName(name: string, type: ModelType): string {
|
||||
const fileType: string = type.replace(/\s+/g, "").toLowerCase();
|
||||
const fileType: string = type.replace(/\s+/g, Constants.EMPTY_STRING).toLowerCase();
|
||||
return `${name}.${fileType}.json`;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as parser from "jsonc-parser";
|
||||
import * as vscode from "vscode";
|
||||
import { IntelliSenseUtility } from "./intelliSenseUtility";
|
||||
|
||||
export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemProvider {
|
||||
public provideCompletionItems(
|
||||
|
@ -10,6 +12,9 @@ export class DigitalTwinCompletionItemProvider implements vscode.CompletionItemP
|
|||
token: vscode.CancellationToken,
|
||||
context: vscode.CompletionContext,
|
||||
): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
|
||||
return null;
|
||||
const jsonNode: parser.Node | undefined = IntelliSenseUtility.parseDigitalTwinModel(document);
|
||||
if (!jsonNode) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,7 @@ import * as vscode from "vscode";
|
|||
import { Constants } from "../common/constants";
|
||||
import { DiagnosticMessage, DigitalTwinConstants } from "./digitalTwinConstants";
|
||||
import { ClassNode, DigitalTwinGraph, PropertyNode, ValueSchema } from "./digitalTwinGraph";
|
||||
import { IntelliSenseUtility } from "./intelliSenseUtility";
|
||||
|
||||
export enum JsonNodeType {
|
||||
Object = "object",
|
||||
Array = "array",
|
||||
String = "string",
|
||||
Number = "number",
|
||||
Boolean = "boolean",
|
||||
}
|
||||
|
||||
interface PropertyPair {
|
||||
name: parser.Node;
|
||||
value: parser.Node;
|
||||
}
|
||||
import { IntelliSenseUtility, JsonNodeType, PropertyPair } from "./intelliSenseUtility";
|
||||
|
||||
interface Problem {
|
||||
offset: number;
|
||||
|
@ -54,13 +41,6 @@ export class DigitalTwinDiagnosticProvider {
|
|||
return classes;
|
||||
}
|
||||
|
||||
private static parseProperty(jsonNode: parser.Node): PropertyPair | undefined {
|
||||
if (!jsonNode.children || jsonNode.children.length !== 2) {
|
||||
return undefined;
|
||||
}
|
||||
return { name: jsonNode.children[0], value: jsonNode.children[1] };
|
||||
}
|
||||
|
||||
private static getNamePropertyPair(jsonNode: parser.Node): PropertyPair | undefined {
|
||||
if (jsonNode.type !== JsonNodeType.Object || !jsonNode.children || jsonNode.children.length === 0) {
|
||||
return undefined;
|
||||
|
@ -68,7 +48,7 @@ export class DigitalTwinDiagnosticProvider {
|
|||
|
||||
let propertyPair: PropertyPair | undefined;
|
||||
for (const child of jsonNode.children) {
|
||||
propertyPair = DigitalTwinDiagnosticProvider.parseProperty(child);
|
||||
propertyPair = IntelliSenseUtility.parseProperty(child);
|
||||
if (!propertyPair) {
|
||||
continue;
|
||||
}
|
||||
|
@ -235,7 +215,7 @@ export class DigitalTwinDiagnosticProvider {
|
|||
let propertyPair: PropertyPair | undefined;
|
||||
let propertyNode: PropertyNode | undefined;
|
||||
for (const child of jsonNode.children) {
|
||||
propertyPair = DigitalTwinDiagnosticProvider.parseProperty(child);
|
||||
propertyPair = IntelliSenseUtility.parseProperty(child);
|
||||
if (!propertyPair) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface PropertyNode {
|
|||
id: string;
|
||||
label?: string;
|
||||
isArray?: boolean;
|
||||
comment?: string;
|
||||
range?: ClassNode[];
|
||||
constraint?: ConstraintNode;
|
||||
}
|
||||
|
@ -56,7 +57,7 @@ enum EdgeType {
|
|||
Label = "http://www.w3.org/2000/01/rdf-schema#label",
|
||||
Domain = "http://www.w3.org/2000/01/rdf-schema#domain",
|
||||
SubClassOf = "http://www.w3.org/2000/01/rdf-schema#subClassOf",
|
||||
Comment = "http://www.w3.org/2000/01/rdf-schema#comment", // Comment hasn't been used yet in DTDL
|
||||
Comment = "http://www.w3.org/2000/01/rdf-schema#comment",
|
||||
}
|
||||
|
||||
export class DigitalTwinGraph {
|
||||
|
@ -73,7 +74,7 @@ export class DigitalTwinGraph {
|
|||
}
|
||||
|
||||
public static getClassType(classNode: ClassNode): string {
|
||||
return classNode.label ? classNode.label : classNode.id;
|
||||
return classNode.label || classNode.id;
|
||||
}
|
||||
|
||||
public static getValidTypes(propertyNode: PropertyNode): string[] {
|
||||
|
@ -122,12 +123,14 @@ export class DigitalTwinGraph {
|
|||
private propertyNodes: Map<string, PropertyNode>;
|
||||
private contextNodes: Map<string, ContextNode>;
|
||||
private constraintNodes: Map<string, ConstraintNode>;
|
||||
private reversedIndex: Map<string, string>;
|
||||
private vocabulary: string;
|
||||
private constructor() {
|
||||
this.classNodes = new Map<string, ClassNode>();
|
||||
this.propertyNodes = new Map<string, PropertyNode>();
|
||||
this.contextNodes = new Map<string, ContextNode>();
|
||||
this.constraintNodes = new Map<string, ConstraintNode>();
|
||||
this.reversedIndex = new Map<string, string>();
|
||||
this.vocabulary = Constants.EMPTY_STRING;
|
||||
}
|
||||
|
||||
|
@ -139,6 +142,10 @@ export class DigitalTwinGraph {
|
|||
return this.propertyNodes.get(id);
|
||||
}
|
||||
|
||||
public getNodeId(name: string): string {
|
||||
return this.reversedIndex.get(name) || Constants.EMPTY_STRING;
|
||||
}
|
||||
|
||||
private init(context: vscode.ExtensionContext): void {
|
||||
let contextJson;
|
||||
let constraintJson;
|
||||
|
@ -161,17 +168,21 @@ export class DigitalTwinGraph {
|
|||
const context = contextJson[DigitalTwinConstants.CONTEXT];
|
||||
this.vocabulary = context[DigitalTwinConstants.VOCABULARY] as string;
|
||||
|
||||
let id: string;
|
||||
for (const key in context) {
|
||||
if (DigitalTwinGraph.isReservedName(key)) {
|
||||
continue;
|
||||
}
|
||||
const value = context[key];
|
||||
if (typeof value === "string") {
|
||||
this.contextNodes.set(this.getId(value), { name: key, isArray: false });
|
||||
id = this.getId(value);
|
||||
this.contextNodes.set(id, { name: key, isArray: false });
|
||||
} else {
|
||||
const isArray: boolean = DigitalTwinGraph.isArrayType(value);
|
||||
this.contextNodes.set(this.getId(value[DigitalTwinConstants.ID] as string), { name: key, isArray });
|
||||
id = this.getId(value[DigitalTwinConstants.ID] as string);
|
||||
this.contextNodes.set(id, { name: key, isArray });
|
||||
}
|
||||
this.reversedIndex.set(key, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,6 +226,9 @@ export class DigitalTwinGraph {
|
|||
case EdgeType.SubClassOf:
|
||||
this.handleEdgeOfSubClassOf(edge);
|
||||
break;
|
||||
case EdgeType.Comment:
|
||||
this.handleEdgeOfComment(edge);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
@ -324,6 +338,22 @@ export class DigitalTwinGraph {
|
|||
baseClassNode.children.push(classNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle data of Comment edge
|
||||
* 1. set comment of property node
|
||||
* @param edge edge data
|
||||
*/
|
||||
private handleEdgeOfComment(edge: any): void {
|
||||
const id: string = edge.SourceNode.Id as string;
|
||||
const comment: string = edge.TargetNode.Value as string;
|
||||
|
||||
// TODO:(erichen): need to check if comment only exist in property
|
||||
const propertyNode: PropertyNode | undefined = this.propertyNodes.get(id);
|
||||
if (propertyNode) {
|
||||
propertyNode.comment = comment;
|
||||
}
|
||||
}
|
||||
|
||||
private ensureClassNode(id: string): ClassNode {
|
||||
let classNode: ClassNode | undefined = this.classNodes.get(id);
|
||||
if (!classNode) {
|
||||
|
|
|
@ -1,16 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as parser from "jsonc-parser";
|
||||
import * as vscode from "vscode";
|
||||
import { Constants } from "../common/constants";
|
||||
import { DigitalTwinConstants } from "./digitalTwinConstants";
|
||||
import { PropertyNode } from "./digitalTwinGraph";
|
||||
import { IntelliSenseUtility, PropertyPair } from "./intelliSenseUtility";
|
||||
|
||||
export class DigitalTwinHoverProvider implements vscode.HoverProvider {
|
||||
private static getContent(propertyName: string): string {
|
||||
if (!propertyName) {
|
||||
return Constants.EMPTY_STRING;
|
||||
}
|
||||
switch (propertyName) {
|
||||
case DigitalTwinConstants.ID:
|
||||
return `An identifier for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
case DigitalTwinConstants.TYPE:
|
||||
return `The type of ${Constants.CHANNEL_NAME} meta model object`;
|
||||
case DigitalTwinConstants.CONTEXT:
|
||||
return `The context for ${Constants.CHANNEL_NAME} Capability Model or interface`;
|
||||
default:
|
||||
const propertyNode: PropertyNode | undefined = IntelliSenseUtility.getPropertyNode(propertyName);
|
||||
return propertyNode && propertyNode.comment ? propertyNode.comment : Constants.EMPTY_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
public provideHover(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
): vscode.ProviderResult<vscode.Hover> {
|
||||
if (!document) {
|
||||
const jsonNode: parser.Node | undefined = IntelliSenseUtility.parseDigitalTwinModel(document);
|
||||
if (!jsonNode) {
|
||||
return undefined;
|
||||
}
|
||||
const currentNode: parser.Node | undefined = parser.findNodeAtOffset(jsonNode, document.offsetAt(position));
|
||||
// parent is property node
|
||||
if (currentNode && currentNode.parent) {
|
||||
const propertyPair: PropertyPair | undefined = IntelliSenseUtility.parseProperty(currentNode.parent);
|
||||
if (propertyPair) {
|
||||
const content: string = DigitalTwinHoverProvider.getContent(propertyPair.name.value as string);
|
||||
if (content) {
|
||||
return new vscode.Hover(content, IntelliSenseUtility.getNodeRange(document, currentNode.parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,21 @@
|
|||
import * as parser from "jsonc-parser";
|
||||
import * as vscode from "vscode";
|
||||
import { DigitalTwinConstants } from "./digitalTwinConstants";
|
||||
import { JsonNodeType } from "./digitalTwinDiagnosticProvider";
|
||||
import { DigitalTwinGraph, PropertyNode } from "./digitalTwinGraph";
|
||||
|
||||
export enum JsonNodeType {
|
||||
Object = "object",
|
||||
Array = "array",
|
||||
String = "string",
|
||||
Number = "number",
|
||||
Boolean = "boolean",
|
||||
}
|
||||
|
||||
export interface PropertyPair {
|
||||
name: parser.Node;
|
||||
value: parser.Node;
|
||||
}
|
||||
|
||||
export class IntelliSenseUtility {
|
||||
public static initGraph(context: vscode.ExtensionContext): boolean {
|
||||
IntelliSenseUtility.graph = DigitalTwinGraph.getInstance(context);
|
||||
|
@ -40,8 +52,20 @@ export class IntelliSenseUtility {
|
|||
return IntelliSenseUtility.getPropertyNode(DigitalTwinConstants.ENTRY_NODE);
|
||||
}
|
||||
|
||||
public static getPropertyNode(propertyName: string): PropertyNode | undefined {
|
||||
return IntelliSenseUtility.graph.getPropertyNode(propertyName);
|
||||
public static getPropertyNode(name: string): PropertyNode | undefined {
|
||||
const id: string = IntelliSenseUtility.graph.getNodeId(name) || name;
|
||||
return IntelliSenseUtility.graph.getPropertyNode(id);
|
||||
}
|
||||
|
||||
public static parseProperty(jsonNode: parser.Node): PropertyPair | undefined {
|
||||
if (!jsonNode.children || jsonNode.children.length !== 2) {
|
||||
return undefined;
|
||||
}
|
||||
return { name: jsonNode.children[0], value: jsonNode.children[1] };
|
||||
}
|
||||
|
||||
public static getNodeRange(document: vscode.TextDocument, node: parser.Node): vscode.Range {
|
||||
return new vscode.Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
|
||||
}
|
||||
|
||||
private static graph: DigitalTwinGraph;
|
||||
|
|
|
@ -121,7 +121,7 @@ export class ModelRepositoryClient {
|
|||
if (repoInfo.repositoryId) {
|
||||
qs.repositoryId = repoInfo.repositoryId;
|
||||
}
|
||||
const accessToken = repoInfo.accessToken ? repoInfo.accessToken : "";
|
||||
const accessToken = repoInfo.accessToken || Constants.EMPTY_STRING;
|
||||
const options: request.OptionsWithUri = {
|
||||
method,
|
||||
uri,
|
||||
|
|
|
@ -83,7 +83,7 @@ export class ModelRepositoryManager {
|
|||
accessToken: connection.generateAccessToken(),
|
||||
};
|
||||
// test connection by calling searchModel
|
||||
await ModelRepositoryClient.searchModel(repoInfo, ModelType.Interface, "", 1, null);
|
||||
await ModelRepositoryClient.searchModel(repoInfo, ModelType.Interface, Constants.EMPTY_STRING, 1, null);
|
||||
if (newConnection) {
|
||||
await CredentialStore.set(Constants.MODEL_REPOSITORY_CONNECTION_KEY, connectionString);
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ export class ModelRepositoryManager {
|
|||
public async searchModel(
|
||||
type: ModelType,
|
||||
publicRepository: boolean,
|
||||
keyword: string = "",
|
||||
keyword: string = Constants.EMPTY_STRING,
|
||||
pageSize: number = Constants.DEFAULT_PAGE_SIZE,
|
||||
continuationToken: string | null = null,
|
||||
): Promise<SearchResult> {
|
||||
|
|
|
@ -68,7 +68,7 @@ export class UI {
|
|||
}
|
||||
items.push({ label: UIConstants.BROWSE_LABEL, description: "" });
|
||||
const selected: vscode.QuickPickItem = await UI.showQuickPick(label, items);
|
||||
return selected.description ? selected.description : await UI.showOpenDialog(label);
|
||||
return selected.description || (await UI.showOpenDialog(label));
|
||||
}
|
||||
|
||||
public static async showQuickPick(label: string, items: vscode.QuickPickItem[]): Promise<vscode.QuickPickItem> {
|
||||
|
|
Загрузка…
Ссылка в новой задаче