Merge branch 'main' of https://github.com/Digital-EDA/Digital-IDE
merge
This commit is contained in:
commit
d9d26a2c84
30
package-lock.json
generated
30
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"state-machine-cat": "^9.2.5",
|
"state-machine-cat": "^9.2.5",
|
||||||
"temp": "^0.9.4",
|
"temp": "^0.9.4",
|
||||||
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
"vscode-languageclient": "^7.0.0",
|
"vscode-languageclient": "^7.0.0",
|
||||||
"vscode-textmate": "^9.0.0",
|
"vscode-textmate": "^9.0.0",
|
||||||
"wavedrom": "^2.9.1"
|
"wavedrom": "^2.9.1"
|
||||||
@ -3809,11 +3810,11 @@
|
|||||||
"deprecated": "no longer supported"
|
"deprecated": "no longer supported"
|
||||||
},
|
},
|
||||||
"node_modules/vscode-jsonrpc": {
|
"node_modules/vscode-jsonrpc": {
|
||||||
"version": "6.0.0",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz",
|
||||||
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==",
|
"integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0 || >=10.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vscode-languageclient": {
|
"node_modules/vscode-languageclient": {
|
||||||
@ -3838,6 +3839,14 @@
|
|||||||
"vscode-languageserver-types": "3.16.0"
|
"vscode-languageserver-types": "3.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vscode-languageserver-protocol/node_modules/vscode-jsonrpc": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0 || >=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vscode-languageserver-types": {
|
"node_modules/vscode-languageserver-types": {
|
||||||
"version": "3.16.0",
|
"version": "3.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
|
||||||
@ -7124,9 +7133,9 @@
|
|||||||
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ=="
|
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ=="
|
||||||
},
|
},
|
||||||
"vscode-jsonrpc": {
|
"vscode-jsonrpc": {
|
||||||
"version": "6.0.0",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz",
|
||||||
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
|
"integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="
|
||||||
},
|
},
|
||||||
"vscode-languageclient": {
|
"vscode-languageclient": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
@ -7145,6 +7154,13 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"vscode-jsonrpc": "6.0.0",
|
"vscode-jsonrpc": "6.0.0",
|
||||||
"vscode-languageserver-types": "3.16.0"
|
"vscode-languageserver-types": "3.16.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-jsonrpc": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vscode-languageserver-types": {
|
"vscode-languageserver-types": {
|
||||||
|
@ -877,6 +877,7 @@
|
|||||||
],
|
],
|
||||||
"extensions": [
|
"extensions": [
|
||||||
".sv",
|
".sv",
|
||||||
|
".svh",
|
||||||
".SV"
|
".SV"
|
||||||
],
|
],
|
||||||
"configuration": "./config/systemverilog.configuration.json",
|
"configuration": "./config/systemverilog.configuration.json",
|
||||||
@ -1161,8 +1162,9 @@
|
|||||||
"showdown": "^2.1.0",
|
"showdown": "^2.1.0",
|
||||||
"state-machine-cat": "^9.2.5",
|
"state-machine-cat": "^9.2.5",
|
||||||
"temp": "^0.9.4",
|
"temp": "^0.9.4",
|
||||||
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
|
"vscode-languageclient": "^7.0.0",
|
||||||
"vscode-textmate": "^9.0.0",
|
"vscode-textmate": "^9.0.0",
|
||||||
"wavedrom": "^2.9.1",
|
"wavedrom": "^2.9.1"
|
||||||
"vscode-languageclient": "^7.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ async function callParser(path, func) {
|
|||||||
// console.log(res);
|
// console.log(res);
|
||||||
|
|
||||||
debug.compute += Date.now() - s3;
|
debug.compute += Date.now() - s3;
|
||||||
console.log(path, debug);
|
// console.log(path, debug);
|
||||||
return JSON.parse(res);
|
return JSON.parse(res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`errors happen when call wasm, path: ${path}, errors: ${error}, input params: (${path}, ${func})`);
|
console.log(`errors happen when call wasm, path: ${path}, errors: ${error}, input params: (${path}, ${func})`);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
import { opeParam, MainOutput, AbsPath, ReportType } from './global';
|
import { opeParam, MainOutput, AbsPath, ReportType, LspClient } from './global';
|
||||||
import { hdlParam } from './hdlParser';
|
import { hdlParam } from './hdlParser';
|
||||||
import * as manager from './manager';
|
import * as manager from './manager';
|
||||||
import * as func from './function';
|
import * as func from './function';
|
||||||
@ -9,30 +9,42 @@ import { extensionUrl } from '../resources/hdlParser';
|
|||||||
|
|
||||||
import * as lspClient from './function/lsp-client';
|
import * as lspClient from './function/lsp-client';
|
||||||
|
|
||||||
async function registerCommand(context: vscode.ExtensionContext) {
|
|
||||||
manager.registerManagerCommands(context);
|
|
||||||
|
|
||||||
|
|
||||||
|
async function registerCommand(context: vscode.ExtensionContext) {
|
||||||
func.registerFunctionCommands(context);
|
func.registerFunctionCommands(context);
|
||||||
func.registerLsp(context);
|
func.registerLsp(context);
|
||||||
func.registerToolCommands(context);
|
func.registerToolCommands(context);
|
||||||
func.registerFSM(context);
|
func.registerFSM(context);
|
||||||
func.registerNetlist(context);
|
func.registerNetlist(context);
|
||||||
func.registerWaveViewer(context);
|
func.registerWaveViewer(context);
|
||||||
// lspClient.activate(context);
|
|
||||||
|
lspClient.activate(context);
|
||||||
|
await LspClient.MainClient?.onReady();
|
||||||
// lspClient.activateVHDL(context);
|
// lspClient.activateVHDL(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function launch(context: vscode.ExtensionContext) {
|
async function launch(context: vscode.ExtensionContext) {
|
||||||
|
await vscode.window.withProgress({
|
||||||
|
location: vscode.ProgressLocation.Window,
|
||||||
|
title: 'Register Command (Digtial-IDE)'
|
||||||
|
}, async () => {
|
||||||
|
await registerCommand(context);
|
||||||
|
});
|
||||||
|
|
||||||
await vscode.window.withProgress({
|
await vscode.window.withProgress({
|
||||||
location: vscode.ProgressLocation.Window,
|
location: vscode.ProgressLocation.Window,
|
||||||
title: 'Initialization (Digtial-IDE)'
|
title: 'Initialization (Digtial-IDE)'
|
||||||
}, async () => {
|
}, async () => {
|
||||||
await manager.prjManage.initialise(context);
|
await manager.prjManage.initialise(context);
|
||||||
await registerCommand(context);
|
manager.registerManagerCommands(context);
|
||||||
|
|
||||||
hdlMonitor.start();
|
hdlMonitor.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MainOutput.report('Digital-IDE has launched, Version: 0.3.3', ReportType.Launch);
|
MainOutput.report('Digital-IDE has launched, Version: 0.3.3', ReportType.Launch);
|
||||||
MainOutput.report('OS: ' + opeParam.os, ReportType.Launch);
|
MainOutput.report('OS: ' + opeParam.os, ReportType.Launch);
|
||||||
|
|
||||||
@ -61,6 +73,6 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate() {
|
export function deactivate() {
|
||||||
// lspClient.deactivate();
|
lspClient.deactivate();
|
||||||
// lspClient.deactivateVHDL();
|
// lspClient.deactivateVHDL();
|
||||||
}
|
}
|
@ -74,14 +74,14 @@ function registerLsp(context: vscode.ExtensionContext) {
|
|||||||
vscode.commands.registerCommand('digital-ide.vhdl2vlog', uri => lspTranslator.vhdl2vlog(uri));
|
vscode.commands.registerCommand('digital-ide.vhdl2vlog', uri => lspTranslator.vhdl2vlog(uri));
|
||||||
|
|
||||||
// verilog lsp
|
// verilog lsp
|
||||||
vscode.languages.registerDocumentSymbolProvider(vlogSelector, lspDocSymbol.vlogDocSymbolProvider);
|
// vscode.languages.registerDocumentSymbolProvider(vlogSelector, lspDocSymbol.vlogDocSymbolProvider);
|
||||||
vscode.languages.registerDefinitionProvider(vlogSelector, lspDefinition.vlogDefinitionProvider);
|
// vscode.languages.registerDefinitionProvider(vlogSelector, lspDefinition.vlogDefinitionProvider);
|
||||||
vscode.languages.registerHoverProvider(vlogSelector, lspHover.vlogHoverProvider);
|
// vscode.languages.registerHoverProvider(vlogSelector, lspHover.vlogHoverProvider);
|
||||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogIncludeCompletionProvider, '/', '"');
|
// vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogIncludeCompletionProvider, '/', '"');
|
||||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`');
|
// vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogMacroCompletionProvider, '`');
|
||||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
|
// vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogPositionPortProvider, '.');
|
||||||
vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider);
|
// vscode.languages.registerCompletionItemProvider(vlogSelector, lspCompletion.vlogCompletionProvider);
|
||||||
vscode.languages.registerDocumentSemanticTokensProvider(vlogSelector, lspDocSemantic.vlogDocSenmanticProvider, lspDocSemantic.vlogLegend);
|
// vscode.languages.registerDocumentSemanticTokensProvider(vlogSelector, lspDocSemantic.vlogDocSenmanticProvider, lspDocSemantic.vlogLegend);
|
||||||
|
|
||||||
|
|
||||||
// vhdl lsp
|
// vhdl lsp
|
||||||
|
@ -9,9 +9,7 @@ import * as vscode from 'vscode';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { platform } from "os";
|
import { platform } from "os";
|
||||||
|
import { LspClient } from '../../global';
|
||||||
let client: LanguageClient;
|
|
||||||
let vhdlClient: LanguageClient;
|
|
||||||
|
|
||||||
function getLspServerExecutionName() {
|
function getLspServerExecutionName() {
|
||||||
const osname = platform();
|
const osname = platform();
|
||||||
@ -58,21 +56,22 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
client = new LanguageClient(
|
const client = new LanguageClient(
|
||||||
"Digital LSP",
|
"Digital LSP",
|
||||||
"Digital LSP",
|
"Digital LSP",
|
||||||
serverOptions,
|
serverOptions,
|
||||||
clientOptions
|
clientOptions
|
||||||
);
|
);
|
||||||
|
LspClient.MainClient = client;
|
||||||
|
|
||||||
client.start();
|
client.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate(): Thenable<void> | undefined {
|
export function deactivate(): Thenable<void> | undefined {
|
||||||
if (!client) {
|
if (!LspClient.MainClient) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return client.stop();
|
return LspClient.MainClient.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -117,19 +116,20 @@ export function activateVHDL(context: vscode.ExtensionContext) {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
client = new LanguageClient(
|
const client = new LanguageClient(
|
||||||
"Digital LSP VHDL",
|
"Digital LSP VHDL",
|
||||||
"Digital LSP VHDL",
|
"Digital LSP VHDL",
|
||||||
serverOptions,
|
serverOptions,
|
||||||
clientOptions
|
clientOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
LspClient.VhdlClient = client;
|
||||||
client.start();
|
client.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivateVHDL(): Thenable<void> | undefined {
|
export function deactivateVHDL(): Thenable<void> | undefined {
|
||||||
if (!vhdlClient) {
|
if (!LspClient.VhdlClient) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return vhdlClient.stop();
|
return LspClient.VhdlClient.stop();
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import { MainOutput, LspOutput, YosysOutput, WaveViewOutput, ReportType } from '
|
|||||||
|
|
||||||
import * as Enum from './enum';
|
import * as Enum from './enum';
|
||||||
import * as Lang from './lang';
|
import * as Lang from './lang';
|
||||||
|
import { LspClient } from './lsp';
|
||||||
|
|
||||||
type AbsPath = string;
|
type AbsPath = string;
|
||||||
type RelPath = string;
|
type RelPath = string;
|
||||||
@ -24,5 +25,6 @@ export {
|
|||||||
YosysOutput,
|
YosysOutput,
|
||||||
WaveViewOutput,
|
WaveViewOutput,
|
||||||
ReportType,
|
ReportType,
|
||||||
AllowNull
|
AllowNull,
|
||||||
|
LspClient
|
||||||
};
|
};
|
41
src/global/lsp.ts
Normal file
41
src/global/lsp.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { LanguageClient, RequestType, ProtocolConnection } from 'vscode-languageclient/node';
|
||||||
|
import { Fast } from '../../resources/hdlParser';
|
||||||
|
|
||||||
|
|
||||||
|
interface IDigitalIDELspClient {
|
||||||
|
MainClient?: LanguageClient,
|
||||||
|
VhdlClient?: LanguageClient
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LspClient: IDigitalIDELspClient = {
|
||||||
|
MainClient: undefined,
|
||||||
|
VhdlClient: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 构造请求参数
|
||||||
|
* RequestType<P, R, E, RO>
|
||||||
|
* P: 请求的参数类型。
|
||||||
|
* R: 请求的响应类型。
|
||||||
|
* E: 请求的错误类型。
|
||||||
|
* RO: 请求的可选参数类型。
|
||||||
|
*/
|
||||||
|
export const CustomRequestType = new RequestType<void, number, void>('custom/request');
|
||||||
|
export const CustomParamRequestType = new RequestType<ICommonParam, number, void>('custom/paramRequest');
|
||||||
|
export const DoFastRequestType = new RequestType('api/fast');
|
||||||
|
|
||||||
|
export interface ITextDocumentItem {
|
||||||
|
uri: vscode.Uri,
|
||||||
|
languageId: string,
|
||||||
|
version: number,
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICommonParam {
|
||||||
|
param: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDoFastParam {
|
||||||
|
|
||||||
|
}
|
@ -229,7 +229,7 @@ class HdlParam {
|
|||||||
try {
|
try {
|
||||||
const fast = await HdlSymbol.fast(path);
|
const fast = await HdlSymbol.fast(path);
|
||||||
if (fast) {
|
if (fast) {
|
||||||
const languageId = this.alignLanguageId(fast.languageId);
|
const languageId = hdlFile.getLanguageId(path);
|
||||||
new HdlFile(path,
|
new HdlFile(path,
|
||||||
languageId,
|
languageId,
|
||||||
fast.macro,
|
fast.macro,
|
||||||
|
@ -1,9 +1,53 @@
|
|||||||
import { Fast, vlogAll, vlogFast, vhdlAll, svFast, svAll, vhdlFast, All } from '../../resources/hdlParser';
|
import * as vscode from 'vscode';
|
||||||
|
import { Fast, vlogAll, vhdlAll, svAll, vhdlFast, All } from '../../resources/hdlParser';
|
||||||
import { hdlFile } from '../hdlFs';
|
import { hdlFile } from '../hdlFs';
|
||||||
import { HdlLangID } from '../global/enum';
|
import { HdlLangID } from '../global/enum';
|
||||||
import { AbsPath } from '../global';
|
import { AbsPath, LspClient } from '../global';
|
||||||
|
import { DoFastRequestType, ITextDocumentItem, CustomParamRequestType } from '../global/lsp';
|
||||||
|
import { RawHdlModule } from './common';
|
||||||
|
|
||||||
|
async function doFastApi(path: string): Promise<Fast | undefined> {
|
||||||
|
try {
|
||||||
|
const client = LspClient.MainClient;
|
||||||
|
const langID = hdlFile.getLanguageId(path);
|
||||||
|
if (client) {
|
||||||
|
const response = await client.sendRequest(DoFastRequestType, { path }) as { fast: any };
|
||||||
|
if (response.fast instanceof Array) {
|
||||||
|
const rawModules = response.fast as RawHdlModule[];
|
||||||
|
return {
|
||||||
|
content: rawModules,
|
||||||
|
languageId: langID,
|
||||||
|
macro: {
|
||||||
|
errors: [],
|
||||||
|
defines: [],
|
||||||
|
includes: [],
|
||||||
|
invalid: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("error happen when run doFastApi, " + error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function vlogFast(path: string): Promise<Fast | undefined> {
|
||||||
|
const fast = await doFastApi(path);
|
||||||
|
return fast;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function svFast(path: string): Promise<Fast | undefined> {
|
||||||
|
const fast = await doFastApi(path);
|
||||||
|
return fast;
|
||||||
|
}
|
||||||
|
|
||||||
namespace HdlSymbol {
|
namespace HdlSymbol {
|
||||||
|
/**
|
||||||
|
* @description 计算出模块级的信息
|
||||||
|
* @param path 文件绝对路径
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function fast(path: AbsPath): Promise<Fast | undefined> {
|
export function fast(path: AbsPath): Promise<Fast | undefined> {
|
||||||
const langID = hdlFile.getLanguageId(path);
|
const langID = hdlFile.getLanguageId(path);
|
||||||
switch (langID) {
|
switch (langID) {
|
||||||
@ -14,6 +58,11 @@ namespace HdlSymbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 0.4.0 后丢弃
|
||||||
|
* @param path 文件绝对路径
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function all(path: AbsPath): Promise<All | undefined> {
|
export function all(path: AbsPath): Promise<All | undefined> {
|
||||||
const langID = hdlFile.getLanguageId(path);
|
const langID = hdlFile.getLanguageId(path);
|
||||||
switch (langID) {
|
switch (langID) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user