Skip to content

Commit

Permalink
Merge branch 'beatscode-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
PranayAgarwal committed Nov 19, 2017
2 parents 8a6e1d3 + a453b35 commit 17ff9c8
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 68 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ This extension is supported on Linux and Mac OS X 10.10 onwards ([see HHVM compa

This extension adds the following Visual Studio Code settings. These can be set in user preferences (⌘+,) or workspace settings (`.vscode/settings.json`).

* `hack.clientPath`: Absolute path to the `hh_client` executable. This can be left empty if `hh_client` is already in your environment $PATH.
* `hack.clientPath`: Absolute path to the hh_client executable. This can be left empty if hh_client is already in your environment $PATH. A `docker exec` command is supported as well.
* `hack.workspaceRootPath`: Absolute path to the workspace root directory. This will be the VS Code workspace root by default, but can be changed if the project is in a subdirectory or mounted in a Docker container.
* `hack.enableCoverageCheck`: Enable calculation of Hack type coverage percentage for every file and display in status bar (default: `false`).

## Issues
Expand Down
14 changes: 8 additions & 6 deletions package.json
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,14 @@
"title": "Hack configuration",
"properties": {
"hack.clientPath": {
"type": [
"string",
"null"
],
"type": "string",
"default": null,
"description": "Absolute path to the hh_client executable. This can be left empty if hh_client is already in your environment $PATH."
"description": "Absolute path to the hh_client executable. This can be left empty if hh_client is already in your environment $PATH. A `docker exec` command is supported as well."
},
"hack.workspaceRootPath": {
"type": "string",
"default": null,
"description": "Absolute path to the workspace root directory. This will be the VS Code workspace root by default, but can be changed if the project is in a subdirectory or mounted in a Docker container."
},
"hack.enableCoverageCheck": {
"type": "boolean",
Expand Down Expand Up @@ -116,4 +118,4 @@
"vscode": "^1.1.7",
"vscode-languageclient": "^3.5.0"
}
}
}
12 changes: 12 additions & 0 deletions src/Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Loads values from VS Code config. It is currently only read at extension launch,
* but config change watchers can be added here if needed.
*/

import * as vscode from 'vscode';

const hackConfig = vscode.workspace.getConfiguration('hack');
export const hhClient: string = hackConfig.get('clientPath') || 'hh_client';
export const workspace: string = hackConfig.get('workspaceRootPath') || vscode.workspace.rootPath || '';
export const enableCoverageCheck: boolean = hackConfig.get('enableCoverageCheck') || false;
export const useLanguageServer: boolean = hackConfig.get('useLanguageServer') || false;
10 changes: 9 additions & 1 deletion src/coveragechecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import * as vscode from 'vscode';
import * as config from './Config';
import * as hh_client from './proxy';

type UnfilteredTypeCoverageRegion = {
Expand Down Expand Up @@ -31,6 +32,13 @@ export class HackCoverageChecker implements vscode.Disposable {
this.hhvmCoverDiag = vscode.languages.createDiagnosticCollection('hack_coverage');
}

private static mapFromWorkspacePath = (fileName: string) => {
if (config.workspace && vscode.workspace.rootPath) {
return fileName.replace(vscode.workspace.rootPath, config.workspace);
}
return fileName;
}

/**
* Converts a list of covered regions (from hh_client --color) to line/character positions in a file.
*
Expand Down Expand Up @@ -147,7 +155,7 @@ export class HackCoverageChecker implements vscode.Disposable {
this.hhvmCoverDiag.set(vscode.Uri.file(document.fileName), cachedFileDiagnostics);
return;
}
const colorResult = await hh_client.color(document.fileName);
const colorResult = await hh_client.color(HackCoverageChecker.mapFromWorkspacePath(document.fileName));
if (!colorResult) {
this.coverageStatus.hide();
this.hhvmCoverDiag.clear();
Expand Down
22 changes: 6 additions & 16 deletions src/main.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import * as vscode from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import * as config from './Config';
import { HackCoverageChecker } from './coveragechecker';
import * as providers from './providers';
import * as hh_client from './proxy';
Expand All @@ -12,27 +13,17 @@ import { HackTypeChecker } from './typechecker';

export async function activate(context: vscode.ExtensionContext) {

// start local hhvm server if it isn't running already, or show an error message and deactivate extension typecheck & intellisense features if unable to do so
const hhClient = vscode.workspace.getConfiguration('hack').get('clientPath') || 'hh_client';
// check if a compatible verison of hh_client is installed, or show an error message and deactivate extension typecheck & intellisense features
const version = await hh_client.version();
if (!version) {
vscode.window.showErrorMessage(
`Invalid hh_client executable: '${hhClient}'. Please ensure that HHVM is correctly installed or configure an alternate hh_client path in workspace settings.`
`Invalid hh_client executable: '${config.hhClient}'. Please ensure that HHVM is correctly installed or configure an alternate hh_client path in workspace settings.`
);
return;
}

if (version.api_version >= 5 && vscode.workspace.getConfiguration('hack').get('useLanguageServer')) {
const languageClient = new LanguageClient(
'Hack Language Server',
{
command: String(hhClient),
args: ['lsp']
},
{
documentSelector: ['hack']
}
);
if (version.api_version >= 5 && config.useLanguageServer) {
const languageClient = new LanguageClient('Hack Language Server', { command: config.hhClient, args: ['lsp'] }, { documentSelector: ['hack'] });
context.subscriptions.push(languageClient.start());
return;
}
Expand Down Expand Up @@ -60,8 +51,7 @@ export async function activate(context: vscode.ExtensionContext) {
await typechecker.run();

// create coverage checker and run on file open & save, if enabled in settings
const enableCoverageCheck = vscode.workspace.getConfiguration('hack').get('enableCoverageCheck') || false;
if (enableCoverageCheck) {
if (config.enableCoverageCheck) {
await new HackCoverageChecker().start(context);
}
}
Expand Down
65 changes: 40 additions & 25 deletions src/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,10 @@
*/

import * as vscode from 'vscode';
import * as config from './Config';
import * as hh_client from './proxy';
import { OutlineResponse } from './types/hack';

export class HackHoverProvider implements vscode.HoverProvider {
public provideHover(document: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.Hover> {
const wordPosition = document.getWordRangeAtPosition(position);
if (!wordPosition) {
return;
}
const startPosition = wordPosition.start;
const line: number = startPosition.line + 1;
const character: number = startPosition.character + 1;
return hh_client.typeAtPos(document.fileName, line, character).then(hoverType => {
if (!hoverType) {
return;
}
if (hoverType.startsWith('(function')) {
hoverType = hoverType.slice(1, hoverType.length - 1);
}
const formattedMessage: vscode.MarkedString = { language: 'hack', value: hoverType };
return new vscode.Hover(formattedMessage);
});
}
}

const symbolArray = [
{ key: 'function', value: vscode.SymbolKind.Function },
{ key: 'method', value: vscode.SymbolKind.Method },
Expand Down Expand Up @@ -86,6 +65,42 @@ const pushSymbols = (outline: OutlineResponse[], symbols: vscode.SymbolInformati
});
};

const mapFromWorkspacePath = (fileName: string) => {
if (config.workspace && vscode.workspace.rootPath) {
return fileName.replace(vscode.workspace.rootPath, config.workspace);
}
return fileName;
};

const mapToWorkspacePath = (fileName: string) => {
if (config.workspace && vscode.workspace.rootPath) {
return fileName.replace(config.workspace, vscode.workspace.rootPath);
}
return fileName;
};

export class HackHoverProvider implements vscode.HoverProvider {
public provideHover(document: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.Hover> {
const wordPosition = document.getWordRangeAtPosition(position);
if (!wordPosition) {
return;
}
const startPosition = wordPosition.start;
const line: number = startPosition.line + 1;
const character: number = startPosition.character + 1;
return hh_client.typeAtPos(mapFromWorkspacePath(document.fileName), line, character).then(hoverType => {
if (!hoverType) {
return;
}
if (hoverType.startsWith('(function')) {
hoverType = hoverType.slice(1, hoverType.length - 1);
}
const formattedMessage: vscode.MarkedString = { language: 'hack', value: hoverType };
return new vscode.Hover(formattedMessage);
});
}
}

export class HackDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
public provideDocumentSymbols(document: vscode.TextDocument): vscode.ProviderResult<vscode.SymbolInformation[]> {
return hh_client.outline(document.getText()).then(outline => {
Expand All @@ -107,7 +122,7 @@ export class HackWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvid
desc = desc.slice(0, element.desc.indexOf(' in '));
}
const kind = getSymbolKind(desc);
const uri: vscode.Uri = vscode.Uri.file(element.filename);
const uri: vscode.Uri = vscode.Uri.file(mapToWorkspacePath(element.filename));
const container = element.scope || (element.name.includes('\\') ? element.name.slice(0, element.name.lastIndexOf('\\')) : undefined);
const range = getRange(element.line, element.line, element.char_start, element.char_end);
symbols.push(new vscode.SymbolInformation(name, kind, range, uri, container));
Expand Down Expand Up @@ -189,7 +204,7 @@ export class HackReferenceProvider implements vscode.ReferenceProvider {
const locations: vscode.Location[] = [];
foundRefs.forEach(ref => {
const location = new vscode.Location(
vscode.Uri.file(ref.filename),
vscode.Uri.file(mapToWorkspacePath(ref.filename)),
new vscode.Range(
new vscode.Position(ref.line - 1, ref.char_start - 1),
new vscode.Position(ref.line - 1, ref.char_end)));
Expand Down Expand Up @@ -217,7 +232,7 @@ export class HackDefinitionProvider implements vscode.DefinitionProvider {
foundDefinition.forEach(element => {
if (element.definition_pos) {
const location: vscode.Location = new vscode.Location(
vscode.Uri.file(element.definition_pos.filename || document.fileName),
vscode.Uri.file(mapToWorkspacePath(element.definition_pos.filename) || document.fileName),
new vscode.Range(
new vscode.Position(element.definition_pos.line - 1, element.definition_pos.char_start - 1),
new vscode.Position(element.definition_pos.line - 1, element.definition_pos.char_end)));
Expand Down
24 changes: 6 additions & 18 deletions src/proxy.ts
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,9 @@
*/

import * as ps from 'child_process';
import * as vscode from 'vscode';
import * as config from './Config';
import * as hack from './types/hack';

export function start(hhClient: string): boolean {
try {
ps.execFileSync(hhClient, ['start', vscode.workspace.rootPath || '']);
return true;
} catch (err) {
if (err.status === 77) {
// server was already running
return true;
}
return false;
}
}

export async function version(): Promise<hack.Version | undefined> {
return run(['--version']);
}
Expand Down Expand Up @@ -77,11 +64,12 @@ export async function format(text: string, startPos: number, endPos: number): Pr
return run(['--format', startPos.toString(), (endPos + 1).toString()], text);
}

async function run(args: string[], stdin?: string): Promise<any> {
async function run(extraArgs: string[], stdin?: string): Promise<any> {
return new Promise<any>((resolve, _) => {
args = args.concat(['--json', vscode.workspace.rootPath || '']);
const hhClient = vscode.workspace.getConfiguration('hack').get('clientPath') || 'hh_client';
const p = ps.execFile(String(hhClient), args, { maxBuffer: 1024 * 1024 }, (err: any, stdout, stderr) => {
const args = config.hhClient.split(' ');
const command = String(args.shift());
args.push(...extraArgs, '--json', config.workspace);
const p = ps.execFile(command, args, { maxBuffer: 1024 * 1024 }, (err: any, stdout, stderr) => {
if (err !== null && err.code !== 0 && err.code !== 2) {
// any hh_client failure other than typecheck errors
console.error(`Hack: hh_client execution error: ${err}`);
Expand Down
10 changes: 9 additions & 1 deletion src/typechecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import * as vscode from 'vscode';
import * as config from './Config';
import * as hh_client from './proxy';

export class HackTypeChecker {
Expand All @@ -12,6 +13,13 @@ export class HackTypeChecker {
this.hhvmTypeDiag = hhvmTypeDiag;
}

private static mapToWorkspacePath(fileName: string): string {
if (config.workspace && vscode.workspace.rootPath) {
return fileName.replace(config.workspace, vscode.workspace.rootPath);
}
return fileName;
}

public async run() {
const typecheckResult = await hh_client.check();
this.hhvmTypeDiag.clear();
Expand All @@ -38,7 +46,7 @@ export class HackTypeChecker {
vscode.DiagnosticSeverity.Error);
diagnostic.code = code;
diagnostic.source = 'Hack';
const file = error.message[0].path;
const file = HackTypeChecker.mapToWorkspacePath(error.message[0].path);
const cachedFileDiagnostics = diagnosticMap.get(file);
if (cachedFileDiagnostics) {
cachedFileDiagnostics.push(diagnostic);
Expand Down

0 comments on commit 17ff9c8

Please sign in to comment.