feat(intellisense): add hover feature for intelliSense

This commit is contained in:
Eric Chen 2019-10-13 20:17:44 +08:00
Родитель 8b15446cb1
Коммит fc73d089c8
10 изменённых файлов: 114 добавлений и 40 удалений

Просмотреть файл

@ -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> {