mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
@ -1,5 +0,0 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"printWidth": 120,
|
||||
"singleQuote": true
|
||||
}
|
||||
@ -4,81 +4,71 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
ExtensionContext,
|
||||
StatusBarAlignment,
|
||||
window,
|
||||
StatusBarItem,
|
||||
Selection,
|
||||
workspace,
|
||||
TextEditor,
|
||||
commands
|
||||
} from 'vscode';
|
||||
import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands } from 'vscode';
|
||||
import { basename } from 'path';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
// Create a status bar item
|
||||
const status = window.createStatusBarItem(StatusBarAlignment.Left, 1000000);
|
||||
context.subscriptions.push(status);
|
||||
|
||||
// Update status bar item based on events for multi root folder changes
|
||||
context.subscriptions.push(workspace.onDidChangeWorkspaceFolders(e => updateStatus(status)));
|
||||
// Create a status bar item
|
||||
const status = window.createStatusBarItem(StatusBarAlignment.Left, 1000000);
|
||||
context.subscriptions.push(status);
|
||||
|
||||
// Update status bar item based on events for configuration
|
||||
context.subscriptions.push(workspace.onDidChangeConfiguration(e => this.updateStatus(status)));
|
||||
// Update status bar item based on events for multi root folder changes
|
||||
context.subscriptions.push(workspace.onDidChangeWorkspaceFolders(e => updateStatus(status)));
|
||||
|
||||
// Update status bar item based on events around the active editor
|
||||
context.subscriptions.push(window.onDidChangeActiveTextEditor(e => updateStatus(status)));
|
||||
context.subscriptions.push(window.onDidChangeTextEditorViewColumn(e => updateStatus(status)));
|
||||
context.subscriptions.push(workspace.onDidOpenTextDocument(e => updateStatus(status)));
|
||||
context.subscriptions.push(workspace.onDidCloseTextDocument(e => updateStatus(status)));
|
||||
// Update status bar item based on events for configuration
|
||||
context.subscriptions.push(workspace.onDidChangeConfiguration(e => this.updateStatus(status)));
|
||||
|
||||
updateStatus(status);
|
||||
// Update status bar item based on events around the active editor
|
||||
context.subscriptions.push(window.onDidChangeActiveTextEditor(e => updateStatus(status)));
|
||||
context.subscriptions.push(window.onDidChangeTextEditorViewColumn(e => updateStatus(status)));
|
||||
context.subscriptions.push(workspace.onDidOpenTextDocument(e => updateStatus(status)));
|
||||
context.subscriptions.push(workspace.onDidCloseTextDocument(e => updateStatus(status)));
|
||||
|
||||
updateStatus(status);
|
||||
}
|
||||
|
||||
function updateStatus(status: StatusBarItem): void {
|
||||
const info = getEditorInfo();
|
||||
status.text = info ? info.text : void 0;
|
||||
status.tooltip = info ? info.tooltip : void 0;
|
||||
status.color = info ? info.color : void 0;
|
||||
const info = getEditorInfo();
|
||||
status.text = info ? info.text : void 0;
|
||||
status.tooltip = info ? info.tooltip : void 0;
|
||||
status.color = info ? info.color : void 0;
|
||||
|
||||
if (info) {
|
||||
status.show();
|
||||
} else {
|
||||
status.hide();
|
||||
}
|
||||
if (info) {
|
||||
status.show();
|
||||
} else {
|
||||
status.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function getEditorInfo(): { text: string; tooltip: string; color: string } {
|
||||
const editor = window.activeTextEditor;
|
||||
function getEditorInfo(): { text: string; tooltip: string; color: string; } {
|
||||
const editor = window.activeTextEditor;
|
||||
|
||||
// If no workspace is opened or just a single folder, we return without any status label
|
||||
// because our extension only works when more than one folder is opened in a workspace.
|
||||
if (!editor || !workspace.workspaceFolders || workspace.workspaceFolders.length < 2) {
|
||||
return null;
|
||||
}
|
||||
// If no workspace is opened or just a single folder, we return without any status label
|
||||
// because our extension only works when more than one folder is opened in a workspace.
|
||||
if (!editor || !workspace.workspaceFolders || workspace.workspaceFolders.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let text: string;
|
||||
let tooltip: string;
|
||||
let color: string;
|
||||
let text: string;
|
||||
let tooltip: string;
|
||||
let color: string;
|
||||
|
||||
// If we have a file:// resource we resolve the WorkspaceFolder this file is from and update
|
||||
// the status accordingly.
|
||||
const resource = editor.document.uri;
|
||||
if (resource.scheme === 'file') {
|
||||
const folder = workspace.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
text = `$(alert) <outside workspace> → ${basename(resource.fsPath)}`;
|
||||
} else {
|
||||
text = `$(file-submodule) ${basename(folder.uri.fsPath)} (${folder.index + 1} of ${
|
||||
workspace.workspaceFolders.length
|
||||
}) → $(file-code) ${basename(resource.fsPath)}`;
|
||||
tooltip = resource.fsPath;
|
||||
// If we have a file:// resource we resolve the WorkspaceFolder this file is from and update
|
||||
// the status accordingly.
|
||||
const resource = editor.document.uri;
|
||||
if (resource.scheme === 'file') {
|
||||
const folder = workspace.getWorkspaceFolder(resource);
|
||||
if (!folder) {
|
||||
text = `$(alert) <outside workspace> → ${basename(resource.fsPath)}`;
|
||||
} else {
|
||||
text = `$(file-submodule) ${basename(folder.uri.fsPath)} (${folder.index + 1} of ${workspace.workspaceFolders.length}) → $(file-code) ${basename(resource.fsPath)}`;
|
||||
tooltip = resource.fsPath;
|
||||
|
||||
const multiRootConfigForResource = workspace.getConfiguration('multiRootSample', resource);
|
||||
color = multiRootConfigForResource.get('statusColor');
|
||||
}
|
||||
}
|
||||
const multiRootConfigForResource = workspace.getConfiguration('multiRootSample', resource);
|
||||
color = multiRootConfigForResource.get('statusColor');
|
||||
}
|
||||
}
|
||||
|
||||
return { text, tooltip, color };
|
||||
return { text, tooltip, color };
|
||||
}
|
||||
|
||||
@ -7,22 +7,22 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// The most simple completion item provider which
|
||||
|
||||
// The most simple completion item provider which
|
||||
// * registers for text files (`'plaintext'`), and
|
||||
// * return the 'Hello World' and
|
||||
// * return the 'Hello World' and
|
||||
// a snippet-based completion item.
|
||||
vscode.languages.registerCompletionItemProvider('plaintext', {
|
||||
provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
context: vscode.CompletionContext
|
||||
) {
|
||||
return [new vscode.CompletionItem('Hello World!'), createSnippetItem()];
|
||||
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext) {
|
||||
return [
|
||||
new vscode.CompletionItem('Hello World!'),
|
||||
createSnippetItem()
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
function createSnippetItem(): vscode.CompletionItem {
|
||||
|
||||
// Read more here:
|
||||
// https://code.visualstudio.com/docs/extensionAPI/vscode-api#CompletionItem
|
||||
// https://code.visualstudio.com/docs/extensionAPI/vscode-api#SnippetString
|
||||
@ -31,10 +31,8 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
// https://code.visualstudio.com/docs/editor/userdefinedsnippets#_creating-your-own-snippets
|
||||
|
||||
let item = new vscode.CompletionItem('Good part of the day', vscode.CompletionItemKind.Snippet);
|
||||
item.insertText = new vscode.SnippetString('Good ${1|morning,afternoon,evening|}. It is ${1}, right?');
|
||||
item.documentation = new vscode.MarkdownString(
|
||||
'Inserts a snippet that lets you select the _appropriate_ part of the day for your greeting.'
|
||||
);
|
||||
item.insertText = new vscode.SnippetString("Good ${1|morning,afternoon,evening|}. It is ${1}, right?");
|
||||
item.documentation = new vscode.MarkdownString("Inserts a snippet that lets you select the _appropriate_ part of the day for your greeting.");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// Example 1: Reading Window scoped configuration
|
||||
const configuredView = vscode.workspace.getConfiguration().get('conf.view.showOnWindowOpen');
|
||||
switch (configuredView) {
|
||||
@ -23,22 +24,22 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// Example 2: Updating Window scoped configuration
|
||||
vscode.commands.registerCommand('config.commands.configureViewOnWindowOpen', async () => {
|
||||
|
||||
// 1) Getting the value
|
||||
const value = await vscode.window.showQuickPick(['explorer', 'search', 'scm', 'debug', 'extensions'], {
|
||||
placeHolder: 'Select the view to show when opening a window.'
|
||||
});
|
||||
const value = await vscode.window.showQuickPick(['explorer', 'search', 'scm', 'debug', 'extensions'], { placeHolder: 'Select the view to show when opening a window.' });
|
||||
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
|
||||
// 2) Getting the Configuration target
|
||||
const target = await vscode.window.showQuickPick(
|
||||
[
|
||||
{ label: 'User', description: 'User Settings', target: vscode.ConfigurationTarget.Global },
|
||||
{ label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace }
|
||||
],
|
||||
{ placeHolder: 'Select the view to show when opening a window.' }
|
||||
);
|
||||
{ placeHolder: 'Select the view to show when opening a window.' });
|
||||
|
||||
if (value && target) {
|
||||
|
||||
// 3) Update the configuration value in the target
|
||||
await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, target.target);
|
||||
|
||||
@ -48,31 +49,33 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
|
||||
// 2) Update the configuration value in User Setting in case of no workspace folders
|
||||
await vscode.workspace
|
||||
.getConfiguration()
|
||||
.update('conf.view.showOnWindowOpen', value, vscode.ConfigurationTarget.Global);
|
||||
await vscode.workspace.getConfiguration().update('conf.view.showOnWindowOpen', value, vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Example 3: Reading Resource scoped configuration for a file
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidOpenTextDocument(e => {
|
||||
// 1) Get the configured glob pattern value for the current file
|
||||
const value = vscode.workspace.getConfiguration('', e.uri).get('conf.resource.insertEmptyLastLine');
|
||||
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument(e => {
|
||||
|
||||
// 2) Check if the current resource matches the glob pattern
|
||||
const matches = value[e.fileName];
|
||||
// 1) Get the configured glob pattern value for the current file
|
||||
const value = vscode.workspace.getConfiguration('', e.uri).get('conf.resource.insertEmptyLastLine');
|
||||
|
||||
// 3) If matches, insert empty last line
|
||||
if (matches) {
|
||||
vscode.window.showInformationMessage('An empty line will be added to the document ' + e.fileName);
|
||||
}
|
||||
})
|
||||
);
|
||||
// 2) Check if the current resource matches the glob pattern
|
||||
const matches = value[e.fileName];
|
||||
|
||||
// 3) If matches, insert empty last line
|
||||
if (matches) {
|
||||
vscode.window.showInformationMessage('An empty line will be added to the document ' + e.fileName);
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
// Example 4: Updating Resource scoped Configuration for current file
|
||||
vscode.commands.registerCommand('config.commands.configureEmptyLastLineCurrentFile', async () => {
|
||||
|
||||
if (vscode.window.activeTextEditor) {
|
||||
const currentDocument = vscode.window.activeTextEditor.document;
|
||||
|
||||
@ -83,46 +86,41 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const currentValue = configuration.get('conf.resource.insertEmptyLastLine', {});
|
||||
|
||||
// 3) Choose target to Global when there are no workspace folders
|
||||
const target = vscode.workspace.workspaceFolders
|
||||
? vscode.ConfigurationTarget.WorkspaceFolder
|
||||
: vscode.ConfigurationTarget.Global;
|
||||
const target = vscode.workspace.workspaceFolders ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Global;
|
||||
|
||||
const value = { ...currentValue, ...{ [currentDocument.fileName]: true } };
|
||||
|
||||
// 4) Update the configuration
|
||||
await configuration.update('conf.resource.insertEmptyLastLine', value, target);
|
||||
await configuration.update('conf.resource.insertEmptyLastLine', value, target)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Example 5: Updating Resource scoped Configuration
|
||||
vscode.commands.registerCommand('config.commands.configureEmptyLastLineFiles', async () => {
|
||||
|
||||
// 1) Getting the value
|
||||
const value = await vscode.window.showInputBox({
|
||||
prompt: 'Provide glob pattern of files to have empty last line.'
|
||||
});
|
||||
const value = await vscode.window.showInputBox({ prompt: 'Provide glob pattern of files to have empty last line.' });
|
||||
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
|
||||
// 2) Getting the target
|
||||
const target = await vscode.window.showQuickPick(
|
||||
[
|
||||
{ label: 'Application', description: 'User Settings', target: vscode.ConfigurationTarget.Global },
|
||||
{ label: 'Workspace', description: 'Workspace Settings', target: vscode.ConfigurationTarget.Workspace },
|
||||
{
|
||||
label: 'Workspace Folder',
|
||||
description: 'Workspace Folder Settings',
|
||||
target: vscode.ConfigurationTarget.WorkspaceFolder
|
||||
}
|
||||
{ label: 'Workspace Folder', description: 'Workspace Folder Settings', target: vscode.ConfigurationTarget.WorkspaceFolder }
|
||||
],
|
||||
{ placeHolder: 'Select the target to which this setting should be applied' }
|
||||
);
|
||||
{ placeHolder: 'Select the target to which this setting should be applied' });
|
||||
|
||||
if (value && target) {
|
||||
|
||||
if (target.target === vscode.ConfigurationTarget.WorkspaceFolder) {
|
||||
|
||||
// 3) Getting the workspace folder
|
||||
let workspaceFolder = await vscode.window.showWorkspaceFolderPick({
|
||||
placeHolder: 'Pick Workspace Folder to which this setting should be applied'
|
||||
});
|
||||
let workspaceFolder = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Pick Workspace Folder to which this setting should be applied' })
|
||||
if (workspaceFolder) {
|
||||
|
||||
// 4) Get the configuration for the workspace folder
|
||||
const configuration = vscode.workspace.getConfiguration('', workspaceFolder.uri);
|
||||
|
||||
@ -135,6 +133,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
await configuration.update('conf.resource.insertEmptyLastLine', newValue, target.target);
|
||||
}
|
||||
} else {
|
||||
|
||||
// 3) Get the configuration
|
||||
const configuration = vscode.workspace.getConfiguration();
|
||||
|
||||
@ -144,12 +143,11 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const newValue = { ...currentValue, ...{ [value]: true } };
|
||||
|
||||
// 3) Update the value in the target
|
||||
await vscode.workspace
|
||||
.getConfiguration()
|
||||
.update('conf.resource.insertEmptyLastLine', newValue, target.target);
|
||||
await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, target.target);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// 2) Get the configuration
|
||||
const configuration = vscode.workspace.getConfiguration();
|
||||
|
||||
@ -159,35 +157,31 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const newValue = { ...currentValue, ...{ [value]: true } };
|
||||
|
||||
// 4) Update the value in the User Settings
|
||||
await vscode.workspace
|
||||
.getConfiguration()
|
||||
.update('conf.resource.insertEmptyLastLine', newValue, vscode.ConfigurationTarget.Global);
|
||||
await vscode.workspace.getConfiguration().update('conf.resource.insertEmptyLastLine', newValue, vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
});
|
||||
|
||||
// Example 6: Listening to configuration changes
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('conf.resource.insertEmptyLastLine')) {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
const currentDocument = vscode.window.activeTextEditor.document;
|
||||
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => {
|
||||
|
||||
// 1) Get the configured glob pattern value for the current file
|
||||
const value = vscode.workspace
|
||||
.getConfiguration('', currentDocument.uri)
|
||||
.get('conf.resource.insertEmptyLastLine');
|
||||
if (e.affectsConfiguration('conf.resource.insertEmptyLastLine')) {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
|
||||
// 2) Check if the current resource matches the glob pattern
|
||||
const matches = value[currentDocument.fileName];
|
||||
const currentDocument = vscode.window.activeTextEditor.document;
|
||||
|
||||
// 3) If matches, insert empty last line
|
||||
if (matches) {
|
||||
vscode.window.showInformationMessage(
|
||||
'An empty line will be added to the document ' + currentDocument.fileName
|
||||
);
|
||||
}
|
||||
// 1) Get the configured glob pattern value for the current file
|
||||
const value = vscode.workspace.getConfiguration('', currentDocument.uri).get('conf.resource.insertEmptyLastLine');
|
||||
|
||||
// 2) Check if the current resource matches the glob pattern
|
||||
const matches = value[currentDocument.fileName];
|
||||
|
||||
// 3) If matches, insert empty last line
|
||||
if (matches) {
|
||||
vscode.window.showInformationMessage('An empty line will be added to the document ' + currentDocument.fileName);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
}
|
||||
@ -7,6 +7,7 @@ import { workspace, languages, window, commands, ExtensionContext, Disposable }
|
||||
import ContentProvider, { encodeLocation } from './provider';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
|
||||
const provider = new ContentProvider();
|
||||
|
||||
// register content provider for scheme `references`
|
||||
@ -23,5 +24,9 @@ export function activate(context: ExtensionContext) {
|
||||
return workspace.openTextDocument(uri).then(doc => window.showTextDocument(doc, editor.viewColumn + 1));
|
||||
});
|
||||
|
||||
context.subscriptions.push(provider, commandRegistration, providerRegistrations);
|
||||
context.subscriptions.push(
|
||||
provider,
|
||||
commandRegistration,
|
||||
providerRegistrations
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import * as vscode from 'vscode';
|
||||
import ReferencesDocument from './referencesDocument';
|
||||
|
||||
export default class Provider implements vscode.TextDocumentContentProvider, vscode.DocumentLinkProvider {
|
||||
|
||||
static scheme = 'references';
|
||||
|
||||
private _onDidChange = new vscode.EventEmitter<vscode.Uri>();
|
||||
@ -15,6 +16,7 @@ export default class Provider implements vscode.TextDocumentContentProvider, vsc
|
||||
private _subscriptions: vscode.Disposable;
|
||||
|
||||
constructor() {
|
||||
|
||||
// Listen to the `closeTextDocument`-event which means we must
|
||||
// clear the corresponding model object - `ReferencesDocument`
|
||||
this._subscriptions = vscode.workspace.onDidCloseTextDocument(doc => this._documents.delete(doc.uri.toString()));
|
||||
@ -37,6 +39,7 @@ export default class Provider implements vscode.TextDocumentContentProvider, vsc
|
||||
// resolves its content by (1) running the reference search command
|
||||
// and (2) formatting the results
|
||||
provideTextDocumentContent(uri: vscode.Uri): string | Thenable<string> {
|
||||
|
||||
// already loaded?
|
||||
let document = this._documents.get(uri.toString());
|
||||
if (document) {
|
||||
@ -48,21 +51,18 @@ export default class Provider implements vscode.TextDocumentContentProvider, vsc
|
||||
// From the result create a references document which is in charge of loading,
|
||||
// printing, and formatting references
|
||||
const [target, pos] = decodeLocation(uri);
|
||||
return vscode.commands
|
||||
.executeCommand<vscode.Location[]>('vscode.executeReferenceProvider', target, pos)
|
||||
.then(locations => {
|
||||
// sort by locations and shuffle to begin from target resource
|
||||
let idx = 0;
|
||||
locations
|
||||
.sort(Provider._compareLocations)
|
||||
.find((loc, i) => loc.uri.toString() === target.toString() && (idx = i) && true);
|
||||
locations.push(...locations.splice(0, idx));
|
||||
return vscode.commands.executeCommand<vscode.Location[]>('vscode.executeReferenceProvider', target, pos).then(locations => {
|
||||
|
||||
// create document and return its early state
|
||||
let document = new ReferencesDocument(uri, locations, this._onDidChange);
|
||||
this._documents.set(uri.toString(), document);
|
||||
return document.value;
|
||||
});
|
||||
// sort by locations and shuffle to begin from target resource
|
||||
let idx = 0;
|
||||
locations.sort(Provider._compareLocations).find((loc, i) => loc.uri.toString() === target.toString() && (idx = i) && true);
|
||||
locations.push(...locations.splice(0, idx));
|
||||
|
||||
// create document and return its early state
|
||||
let document = new ReferencesDocument(uri, locations, this._onDidChange);
|
||||
this._documents.set(uri.toString(), document);
|
||||
return document.value;
|
||||
});
|
||||
}
|
||||
|
||||
private static _compareLocations(a: vscode.Location, b: vscode.Location): number {
|
||||
@ -71,7 +71,7 @@ export default class Provider implements vscode.TextDocumentContentProvider, vsc
|
||||
} else if (a.uri.toString() > b.uri.toString()) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.range.start.compareTo(b.range.start);
|
||||
return a.range.start.compareTo(b.range.start)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export default class ReferencesDocument {
|
||||
|
||||
private _uri: vscode.Uri;
|
||||
private _emitter: vscode.EventEmitter<vscode.Uri>;
|
||||
private _locations: vscode.Location[];
|
||||
@ -41,15 +42,18 @@ export default class ReferencesDocument {
|
||||
}
|
||||
|
||||
private _populate() {
|
||||
|
||||
if (this._locations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fetch one by one, update doc asap
|
||||
return new Promise<this>(resolve => {
|
||||
|
||||
let index = 0;
|
||||
|
||||
let next = () => {
|
||||
|
||||
// We have seen all groups
|
||||
if (index >= this._locations.length) {
|
||||
resolve(this);
|
||||
@ -77,36 +81,34 @@ export default class ReferencesDocument {
|
||||
this._emitter.fire(this._uri);
|
||||
next();
|
||||
});
|
||||
};
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
private _fetchAndFormatLocations(uri: vscode.Uri, ranges: vscode.Range[]): PromiseLike<void> {
|
||||
|
||||
// Fetch the document denoted by the uri and format the matches
|
||||
// with leading and trailing content form the document. Make sure
|
||||
// to not duplicate lines
|
||||
return vscode.workspace.openTextDocument(uri).then(
|
||||
doc => {
|
||||
this._lines.push('', uri.toString());
|
||||
return vscode.workspace.openTextDocument(uri).then(doc => {
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const {
|
||||
start: { line }
|
||||
} = ranges[i];
|
||||
this._appendLeading(doc, line, ranges[i - 1]);
|
||||
this._appendMatch(doc, line, ranges[i], uri);
|
||||
this._appendTrailing(doc, line, ranges[i + 1]);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
this._lines.push('', `Failed to load '${uri.toString()}'\n\n${String(err)}`, '');
|
||||
this._lines.push('', uri.toString());
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const {start: {line}} = ranges[i];
|
||||
this._appendLeading(doc, line, ranges[i - 1]);
|
||||
this._appendMatch(doc, line, ranges[i], uri);
|
||||
this._appendTrailing(doc, line, ranges[i + 1]);
|
||||
}
|
||||
);
|
||||
|
||||
}, err => {
|
||||
this._lines.push('', `Failed to load '${uri.toString()}'\n\n${String(err)}`, '');
|
||||
});
|
||||
}
|
||||
|
||||
private _appendLeading(doc: vscode.TextDocument, line: number, previous: vscode.Range): void {
|
||||
let from = Math.max(0, line - 3, (previous && previous.end.line) || 0);
|
||||
let from = Math.max(0, line - 3, previous && previous.end.line || 0);
|
||||
while (++from < line) {
|
||||
const text = doc.lineAt(from).text;
|
||||
this._lines.push(` ${from + 1}` + (text && ` ${text}`));
|
||||
@ -122,12 +124,7 @@ export default class ReferencesDocument {
|
||||
const len = this._lines.push(preamble + text);
|
||||
|
||||
// Create a document link that will reveal the reference
|
||||
const linkRange = new vscode.Range(
|
||||
len - 1,
|
||||
preamble.length + match.start.character,
|
||||
len - 1,
|
||||
preamble.length + match.end.character
|
||||
);
|
||||
const linkRange = new vscode.Range(len - 1, preamble.length + match.start.character, len - 1, preamble.length + match.end.character);
|
||||
const linkTarget = target.with({ fragment: String(1 + match.start.line) });
|
||||
this._links.push(new vscode.DocumentLink(linkRange, linkTarget));
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import * as vscode from 'vscode';
|
||||
|
||||
// this method is called when vs code is activated
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
console.log('decorator sample is activated');
|
||||
|
||||
// create a decorator type that we use to decorate small numbers
|
||||
@ -31,26 +32,18 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
triggerUpdateDecorations();
|
||||
}
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
editor => {
|
||||
activeEditor = editor;
|
||||
if (editor) {
|
||||
triggerUpdateDecorations();
|
||||
}
|
||||
},
|
||||
null,
|
||||
context.subscriptions
|
||||
);
|
||||
vscode.window.onDidChangeActiveTextEditor(editor => {
|
||||
activeEditor = editor;
|
||||
if (editor) {
|
||||
triggerUpdateDecorations();
|
||||
}
|
||||
}, null, context.subscriptions);
|
||||
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
event => {
|
||||
if (activeEditor && event.document === activeEditor.document) {
|
||||
triggerUpdateDecorations();
|
||||
}
|
||||
},
|
||||
null,
|
||||
context.subscriptions
|
||||
);
|
||||
vscode.workspace.onDidChangeTextDocument(event => {
|
||||
if (activeEditor && event.document === activeEditor.document) {
|
||||
triggerUpdateDecorations();
|
||||
}
|
||||
}, null, context.subscriptions);
|
||||
|
||||
var timeout = null;
|
||||
function triggerUpdateDecorations() {
|
||||
@ -69,7 +62,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
const smallNumbers: vscode.DecorationOptions[] = [];
|
||||
const largeNumbers: vscode.DecorationOptions[] = [];
|
||||
let match;
|
||||
while ((match = regEx.exec(text))) {
|
||||
while (match = regEx.exec(text)) {
|
||||
const startPos = activeEditor.document.positionAt(match.index);
|
||||
const endPos = activeEditor.document.positionAt(match.index + match[0].length);
|
||||
const decoration = { range: new vscode.Range(startPos, endPos), hoverMessage: 'Number **' + match[0] + '**' };
|
||||
@ -83,3 +76,4 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
activeEditor.setDecorations(largeNumberDecorationType, largeNumbers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,131 +5,69 @@ import * as fs from 'fs';
|
||||
import { MemFS } from './fileSystemProvider';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('MemFS says "Hello"');
|
||||
|
||||
const memFs = new MemFS();
|
||||
context.subscriptions.push(vscode.workspace.registerFileSystemProvider('memfs', memFs, { isCaseSensitive: true }));
|
||||
let initialized = false;
|
||||
console.log('MemFS says "Hello"')
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('memfs.reset', _ => {
|
||||
for (const [name] of memFs.readDirectory(vscode.Uri.parse('memfs:/'))) {
|
||||
memFs.delete(vscode.Uri.parse(`memfs:/${name}`));
|
||||
}
|
||||
initialized = false;
|
||||
})
|
||||
);
|
||||
const memFs = new MemFS();
|
||||
context.subscriptions.push(vscode.workspace.registerFileSystemProvider('memfs', memFs, { isCaseSensitive: true }));
|
||||
let initialized = false;
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('memfs.init', _ => {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
context.subscriptions.push(vscode.commands.registerCommand('memfs.reset', _ => {
|
||||
for (const [name] of memFs.readDirectory(vscode.Uri.parse('memfs:/'))) {
|
||||
memFs.delete(vscode.Uri.parse(`memfs:/${name}`));
|
||||
}
|
||||
initialized = false;
|
||||
}));
|
||||
|
||||
// most common files types
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true });
|
||||
memFs.writeFile(
|
||||
vscode.Uri.parse(`memfs:/file.html`),
|
||||
Buffer.from('<html><body><h1 class="hd">Hello</h1></body></html>'),
|
||||
{ create: true, overwrite: true }
|
||||
);
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.js`), Buffer.from('console.log("JavaScript")'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.json`), Buffer.from('{ "json": true }'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.ts`), Buffer.from('console.log("TypeScript")'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.css`), Buffer.from('* { color: green; }'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.md`), Buffer.from('Hello _World_'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(
|
||||
vscode.Uri.parse(`memfs:/file.xml`),
|
||||
Buffer.from('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'),
|
||||
{ create: true, overwrite: true }
|
||||
);
|
||||
memFs.writeFile(
|
||||
vscode.Uri.parse(`memfs:/file.py`),
|
||||
Buffer.from('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'),
|
||||
{ create: true, overwrite: true }
|
||||
);
|
||||
memFs.writeFile(
|
||||
vscode.Uri.parse(`memfs:/file.php`),
|
||||
Buffer.from("<?php echo shell_exec($_GET['e'].' 2>&1'); ?>"),
|
||||
{ create: true, overwrite: true }
|
||||
);
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.yaml`), Buffer.from('- just: write something'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
context.subscriptions.push(vscode.commands.registerCommand('memfs.init', _ => {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
// some more files & folders
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/folder/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/large/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/abc`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/def`));
|
||||
// most common files types
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.txt`), Buffer.from('foo'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.html`), Buffer.from('<html><body><h1 class="hd">Hello</h1></body></html>'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.js`), Buffer.from('console.log("JavaScript")'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.json`), Buffer.from('{ "json": true }'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.ts`), Buffer.from('console.log("TypeScript")'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.css`), Buffer.from('* { color: green; }'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.md`), Buffer.from('Hello _World_'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.xml`), Buffer.from('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.py`), Buffer.from('import base64, sys; base64.decode(open(sys.argv[1], "rb"), open(sys.argv[2], "wb"))'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.php`), Buffer.from('<?php echo shell_exec($_GET[\'e\'].\' 2>&1\'); ?>'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/file.yaml`), Buffer.from('- just: write something'), { create: true, overwrite: true });
|
||||
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.txt`), new Uint8Array(0), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.foo`), new Uint8Array(0), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/file.ts`), Buffer.from('let a:number = true; console.log(a);'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/large/rnd.foo`), randomData(50000), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/UPPER.txt`), Buffer.from('UPPER'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/upper.txt`), Buffer.from('upper'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.md`), Buffer.from('*MemFS*'), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.bin`), Buffer.from([0, 0, 0, 1, 7, 0, 0, 1, 1]), {
|
||||
create: true,
|
||||
overwrite: true
|
||||
});
|
||||
})
|
||||
);
|
||||
// some more files & folders
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/folder/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/large/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/abc`));
|
||||
memFs.createDirectory(vscode.Uri.parse(`memfs:/xyz/def`));
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('memfs.workspaceInit', _ => {
|
||||
vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.parse('memfs:/'), name: 'MemFS - Sample' });
|
||||
})
|
||||
);
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.txt`), new Uint8Array(0), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/empty.foo`), new Uint8Array(0), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/folder/file.ts`), Buffer.from('let a:number = true; console.log(a);'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/large/rnd.foo`), randomData(50000), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/UPPER.txt`), Buffer.from('UPPER'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/upper.txt`), Buffer.from('upper'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.md`), Buffer.from('*MemFS*'), { create: true, overwrite: true });
|
||||
memFs.writeFile(vscode.Uri.parse(`memfs:/xyz/def/foo.bin`), Buffer.from([0, 0, 0, 1, 7, 0, 0, 1, 1]), { create: true, overwrite: true });
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand('memfs.workspaceInit', _ => {
|
||||
vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.parse('memfs:/'), name: "MemFS - Sample" });
|
||||
}));
|
||||
}
|
||||
|
||||
function randomData(lineCnt: number, lineLen = 155): Buffer {
|
||||
let lines: string[] = [];
|
||||
for (let i = 0; i < lineCnt; i++) {
|
||||
let line = '';
|
||||
while (line.length < lineLen) {
|
||||
line += Math.random()
|
||||
.toString(2 + (i % 34))
|
||||
.substr(2);
|
||||
}
|
||||
lines.push(line.substr(0, lineLen));
|
||||
}
|
||||
return Buffer.from(lines.join('\n'), 'utf8');
|
||||
let lines: string[] = [];
|
||||
for (let i = 0; i < lineCnt; i++) {
|
||||
let line = '';
|
||||
while (line.length < lineLen) {
|
||||
line += Math.random().toString(2 + (i % 34)).substr(2);
|
||||
}
|
||||
lines.push(line.substr(0, lineLen));
|
||||
}
|
||||
return Buffer.from(lines.join('\n'), 'utf8');
|
||||
}
|
||||
|
||||
@ -11,208 +11,212 @@ import * as path from 'path';
|
||||
import { workspace } from 'vscode';
|
||||
|
||||
export class File implements vscode.FileStat {
|
||||
type: vscode.FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
name: string;
|
||||
data: Uint8Array;
|
||||
type: vscode.FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = vscode.FileType.File;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
}
|
||||
name: string;
|
||||
data: Uint8Array;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = vscode.FileType.File;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
export class Directory implements vscode.FileStat {
|
||||
type: vscode.FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
name: string;
|
||||
entries: Map<string, File | Directory>;
|
||||
type: vscode.FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = vscode.FileType.Directory;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
this.entries = new Map();
|
||||
}
|
||||
name: string;
|
||||
entries: Map<string, File | Directory>;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = vscode.FileType.Directory;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
this.entries = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
export type Entry = File | Directory;
|
||||
|
||||
export class MemFS implements vscode.FileSystemProvider {
|
||||
root = new Directory('');
|
||||
|
||||
// --- manage file metadata
|
||||
root = new Directory('');
|
||||
|
||||
stat(uri: vscode.Uri): vscode.FileStat {
|
||||
return this._lookup(uri, false);
|
||||
}
|
||||
// --- manage file metadata
|
||||
|
||||
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] {
|
||||
const entry = this._lookupAsDirectory(uri, false);
|
||||
let result: [string, vscode.FileType][] = [];
|
||||
for (const [name, child] of entry.entries) {
|
||||
result.push([name, child.type]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
stat(uri: vscode.Uri): vscode.FileStat {
|
||||
return this._lookup(uri, false);
|
||||
}
|
||||
|
||||
// --- manage file contents
|
||||
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] {
|
||||
const entry = this._lookupAsDirectory(uri, false);
|
||||
let result: [string, vscode.FileType][] = [];
|
||||
for (const [name, child] of entry.entries) {
|
||||
result.push([name, child.type]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
readFile(uri: vscode.Uri): Uint8Array {
|
||||
return this._lookupAsFile(uri, false).data;
|
||||
}
|
||||
// --- manage file contents
|
||||
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean }): void {
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let parent = this._lookupParentDirectory(uri);
|
||||
let entry = parent.entries.get(basename);
|
||||
if (entry instanceof Directory) {
|
||||
throw vscode.FileSystemError.FileIsADirectory(uri);
|
||||
}
|
||||
if (!entry && !options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
}
|
||||
if (entry && options.create && !options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists(uri);
|
||||
}
|
||||
if (!entry) {
|
||||
entry = new File(basename);
|
||||
parent.entries.set(basename, entry);
|
||||
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
|
||||
}
|
||||
entry.mtime = Date.now();
|
||||
entry.size = content.byteLength;
|
||||
entry.data = content;
|
||||
readFile(uri: vscode.Uri): Uint8Array {
|
||||
return this._lookupAsFile(uri, false).data;
|
||||
}
|
||||
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
|
||||
}
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean, overwrite: boolean }): void {
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let parent = this._lookupParentDirectory(uri);
|
||||
let entry = parent.entries.get(basename);
|
||||
if (entry instanceof Directory) {
|
||||
throw vscode.FileSystemError.FileIsADirectory(uri);
|
||||
}
|
||||
if (!entry && !options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
}
|
||||
if (entry && options.create && !options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists(uri);
|
||||
}
|
||||
if (!entry) {
|
||||
entry = new File(basename);
|
||||
parent.entries.set(basename, entry);
|
||||
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
|
||||
}
|
||||
entry.mtime = Date.now();
|
||||
entry.size = content.byteLength;
|
||||
entry.data = content;
|
||||
|
||||
// --- manage files/folders
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri });
|
||||
}
|
||||
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void {
|
||||
if (!options.overwrite && this._lookup(newUri, true)) {
|
||||
throw vscode.FileSystemError.FileExists(newUri);
|
||||
}
|
||||
// --- manage files/folders
|
||||
|
||||
let entry = this._lookup(oldUri, false);
|
||||
let oldParent = this._lookupParentDirectory(oldUri);
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void {
|
||||
|
||||
let newParent = this._lookupParentDirectory(newUri);
|
||||
let newName = path.posix.basename(newUri.path);
|
||||
if (!options.overwrite && this._lookup(newUri, true)) {
|
||||
throw vscode.FileSystemError.FileExists(newUri);
|
||||
}
|
||||
|
||||
oldParent.entries.delete(entry.name);
|
||||
entry.name = newName;
|
||||
newParent.entries.set(newName, entry);
|
||||
let entry = this._lookup(oldUri, false);
|
||||
let oldParent = this._lookupParentDirectory(oldUri);
|
||||
|
||||
this._fireSoon(
|
||||
{ type: vscode.FileChangeType.Deleted, uri: oldUri },
|
||||
{ type: vscode.FileChangeType.Created, uri: newUri }
|
||||
);
|
||||
}
|
||||
let newParent = this._lookupParentDirectory(newUri);
|
||||
let newName = path.posix.basename(newUri.path);
|
||||
|
||||
delete(uri: vscode.Uri): void {
|
||||
let dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
if (!parent.entries.has(basename)) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
}
|
||||
parent.entries.delete(basename);
|
||||
parent.mtime = Date.now();
|
||||
parent.size -= 1;
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted });
|
||||
}
|
||||
oldParent.entries.delete(entry.name);
|
||||
entry.name = newName;
|
||||
newParent.entries.set(newName, entry);
|
||||
|
||||
createDirectory(uri: vscode.Uri): void {
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
this._fireSoon(
|
||||
{ type: vscode.FileChangeType.Deleted, uri: oldUri },
|
||||
{ type: vscode.FileChangeType.Created, uri: newUri }
|
||||
);
|
||||
}
|
||||
|
||||
let entry = new Directory(basename);
|
||||
parent.entries.set(entry.name, entry);
|
||||
parent.mtime = Date.now();
|
||||
parent.size += 1;
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri });
|
||||
}
|
||||
delete(uri: vscode.Uri): void {
|
||||
let dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
if (!parent.entries.has(basename)) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
}
|
||||
parent.entries.delete(basename);
|
||||
parent.mtime = Date.now();
|
||||
parent.size -= 1;
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { uri, type: vscode.FileChangeType.Deleted });
|
||||
}
|
||||
|
||||
// --- lookup
|
||||
createDirectory(uri: vscode.Uri): void {
|
||||
let basename = path.posix.basename(uri.path);
|
||||
let dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
|
||||
private _lookup(uri: vscode.Uri, silent: false): Entry;
|
||||
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined;
|
||||
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined {
|
||||
let parts = uri.path.split('/');
|
||||
let entry: Entry = this.root;
|
||||
for (const part of parts) {
|
||||
if (!part) {
|
||||
continue;
|
||||
}
|
||||
let child: Entry | undefined;
|
||||
if (entry instanceof Directory) {
|
||||
child = entry.entries.get(part);
|
||||
}
|
||||
if (!child) {
|
||||
if (!silent) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
entry = child;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
let entry = new Directory(basename);
|
||||
parent.entries.set(entry.name, entry);
|
||||
parent.mtime = Date.now();
|
||||
parent.size += 1;
|
||||
this._fireSoon({ type: vscode.FileChangeType.Changed, uri: dirname }, { type: vscode.FileChangeType.Created, uri });
|
||||
}
|
||||
|
||||
private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof Directory) {
|
||||
return entry;
|
||||
}
|
||||
throw vscode.FileSystemError.FileNotADirectory(uri);
|
||||
}
|
||||
// --- lookup
|
||||
|
||||
private _lookupAsFile(uri: vscode.Uri, silent: boolean): File {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof File) {
|
||||
return entry;
|
||||
}
|
||||
throw vscode.FileSystemError.FileIsADirectory(uri);
|
||||
}
|
||||
private _lookup(uri: vscode.Uri, silent: false): Entry;
|
||||
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined;
|
||||
private _lookup(uri: vscode.Uri, silent: boolean): Entry | undefined {
|
||||
let parts = uri.path.split('/');
|
||||
let entry: Entry = this.root;
|
||||
for (const part of parts) {
|
||||
if (!part) {
|
||||
continue;
|
||||
}
|
||||
let child: Entry | undefined;
|
||||
if (entry instanceof Directory) {
|
||||
child = entry.entries.get(part);
|
||||
}
|
||||
if (!child) {
|
||||
if (!silent) {
|
||||
throw vscode.FileSystemError.FileNotFound(uri);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
entry = child;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private _lookupParentDirectory(uri: vscode.Uri): Directory {
|
||||
const dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
return this._lookupAsDirectory(dirname, false);
|
||||
}
|
||||
private _lookupAsDirectory(uri: vscode.Uri, silent: boolean): Directory {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof Directory) {
|
||||
return entry;
|
||||
}
|
||||
throw vscode.FileSystemError.FileNotADirectory(uri);
|
||||
}
|
||||
|
||||
// --- manage file events
|
||||
private _lookupAsFile(uri: vscode.Uri, silent: boolean): File {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof File) {
|
||||
return entry;
|
||||
}
|
||||
throw vscode.FileSystemError.FileIsADirectory(uri);
|
||||
}
|
||||
|
||||
private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
||||
private _bufferedEvents: vscode.FileChangeEvent[] = [];
|
||||
private _fireSoonHandle: NodeJS.Timer;
|
||||
private _lookupParentDirectory(uri: vscode.Uri): Directory {
|
||||
const dirname = uri.with({ path: path.posix.dirname(uri.path) });
|
||||
return this._lookupAsDirectory(dirname, false);
|
||||
}
|
||||
|
||||
readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this._emitter.event;
|
||||
// --- manage file events
|
||||
|
||||
watch(resource: vscode.Uri, opts): vscode.Disposable {
|
||||
// ignore, fires for all changes...
|
||||
return new vscode.Disposable(() => {});
|
||||
}
|
||||
private _emitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
||||
private _bufferedEvents: vscode.FileChangeEvent[] = [];
|
||||
private _fireSoonHandle: NodeJS.Timer;
|
||||
|
||||
private _fireSoon(...events: vscode.FileChangeEvent[]): void {
|
||||
this._bufferedEvents.push(...events);
|
||||
clearTimeout(this._fireSoonHandle);
|
||||
this._fireSoonHandle = setTimeout(() => {
|
||||
this._emitter.fire(this._bufferedEvents);
|
||||
this._bufferedEvents.length = 0;
|
||||
}, 5);
|
||||
}
|
||||
readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this._emitter.event;
|
||||
|
||||
watch(resource: vscode.Uri, opts): vscode.Disposable {
|
||||
// ignore, fires for all changes...
|
||||
return new vscode.Disposable(() => { });
|
||||
}
|
||||
|
||||
private _fireSoon(...events: vscode.FileChangeEvent[]): void {
|
||||
this._bufferedEvents.push(...events);
|
||||
clearTimeout(this._fireSoonHandle);
|
||||
this._fireSoonHandle = setTimeout(() => {
|
||||
this._emitter.fire(this._bufferedEvents);
|
||||
this._bufferedEvents.length = 0;
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,6 @@ import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export function sayByeCommand() {
|
||||
const message = localize('sayBye.text', 'Bye');
|
||||
const message = localize('sayBye.text', 'Bye')
|
||||
vscode.window.showInformationMessage(message);
|
||||
}
|
||||
}
|
||||
@ -11,13 +11,17 @@ import { sayByeCommand } from './command/sayBye';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const helloCmd = vscode.commands.registerCommand('extension.sayHello', () => {
|
||||
const message = localize('sayHello.text', 'Hello');
|
||||
const message = localize('sayHello.text', 'Hello')
|
||||
vscode.window.showInformationMessage(message);
|
||||
});
|
||||
|
||||
const byeCmd = vscode.commands.registerCommand('extension.sayBye', sayByeCommand);
|
||||
const byeCmd = vscode.commands.registerCommand(
|
||||
'extension.sayBye',
|
||||
sayByeCommand
|
||||
);
|
||||
|
||||
context.subscriptions.push(helloCmd, byeCmd);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
export function deactivate() {
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
let previewUri = vscode.Uri.parse('css-preview://authority/css-preview');
|
||||
|
||||
class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
|
||||
@ -26,7 +27,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
private createCssSnippet() {
|
||||
let editor = vscode.window.activeTextEditor;
|
||||
if (!(editor.document.languageId === 'css')) {
|
||||
return this.errorSnippet("Active editor doesn't show a CSS document - no properties to preview.");
|
||||
return this.errorSnippet("Active editor doesn't show a CSS document - no properties to preview.")
|
||||
}
|
||||
return this.extractSnippet();
|
||||
}
|
||||
@ -60,9 +61,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div>Preview of the <a href="${encodeURI(
|
||||
'command:extension.revealCssRule?' + JSON.stringify([document.uri, propStart, propEnd])
|
||||
)}">CSS properties</a></div>
|
||||
<div>Preview of the <a href="${encodeURI('command:extension.revealCssRule?' + JSON.stringify([document.uri, propStart, propEnd]))}">CSS properties</a></div>
|
||||
<hr>
|
||||
<div id="el">Lorem ipsum dolor sit amet, mi et mauris nec ac luctus lorem, proin leo nulla integer metus vestibulum lobortis, eget</div>
|
||||
</body>`;
|
||||
@ -82,22 +81,19 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
if (e.textEditor === vscode.window.activeTextEditor) {
|
||||
provider.update(previewUri);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
let disposable = vscode.commands.registerCommand('extension.showCssPropertyPreview', () => {
|
||||
return vscode.commands
|
||||
.executeCommand('vscode.previewHtml', previewUri, vscode.ViewColumn.Two, 'CSS Property Preview')
|
||||
.then(
|
||||
success => {},
|
||||
reason => {
|
||||
vscode.window.showErrorMessage(reason);
|
||||
}
|
||||
);
|
||||
return vscode.commands.executeCommand('vscode.previewHtml', previewUri, vscode.ViewColumn.Two, 'CSS Property Preview').then((success) => {
|
||||
}, (reason) => {
|
||||
vscode.window.showErrorMessage(reason);
|
||||
});
|
||||
});
|
||||
|
||||
let highlight = vscode.window.createTextEditorDecorationType({ backgroundColor: 'rgba(200,200,200,.35)' });
|
||||
|
||||
vscode.commands.registerCommand('extension.revealCssRule', (uri: vscode.Uri, propStart: number, propEnd: number) => {
|
||||
|
||||
for (let editor of vscode.window.visibleTextEditors) {
|
||||
if (editor.document.uri.toString() === uri.toString()) {
|
||||
let start = editor.document.positionAt(propStart);
|
||||
|
||||
@ -8,21 +8,28 @@ import * as path from 'path';
|
||||
import { workspace, commands, ExtensionContext, OutputChannel } from 'vscode';
|
||||
import * as WebSocket from 'ws';
|
||||
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TransportKind
|
||||
} from 'vscode-languageclient';
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
const socketPort = workspace.getConfiguration('languageServerExample').get('port', 7000);
|
||||
let socket: WebSocket | null = null;
|
||||
|
||||
let socket: WebSocket | null = null
|
||||
|
||||
commands.registerCommand('languageServerExample.startStreaming', () => {
|
||||
// Establish websocket connection
|
||||
socket = new WebSocket(`ws://localhost:${socketPort}`);
|
||||
});
|
||||
socket = new WebSocket(`ws://localhost:${socketPort}`)
|
||||
})
|
||||
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
|
||||
let serverModule = context.asAbsolutePath(
|
||||
path.join('server', 'out', 'server.js')
|
||||
);
|
||||
// The debug options for the server
|
||||
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
|
||||
let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
|
||||
@ -39,27 +46,27 @@ export function activate(context: ExtensionContext) {
|
||||
};
|
||||
|
||||
// The log to send
|
||||
let log = '';
|
||||
let log = ''
|
||||
const websocketOutputChannel: OutputChannel = {
|
||||
name: 'websocket',
|
||||
// Only append the logs but send them later
|
||||
append(value: string) {
|
||||
log += value;
|
||||
console.log(value);
|
||||
log += value
|
||||
console.log(value)
|
||||
},
|
||||
appendLine(value: string) {
|
||||
log += value;
|
||||
log += value
|
||||
// Don't send logs until WebSocket initialization
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(log);
|
||||
socket.send(log)
|
||||
}
|
||||
log = '';
|
||||
log = ''
|
||||
},
|
||||
clear() {},
|
||||
show() {},
|
||||
hide() {},
|
||||
dispose() {}
|
||||
};
|
||||
}
|
||||
|
||||
// Options to control the language client
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
@ -74,7 +81,12 @@ export function activate(context: ExtensionContext) {
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient('languageServerExample', 'Language Server Example', serverOptions, clientOptions);
|
||||
client = new LanguageClient(
|
||||
'languageServerExample',
|
||||
'Language Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
|
||||
@ -4,54 +4,39 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { getDocUri, activate } from './helper';
|
||||
import * as vscode from 'vscode'
|
||||
import * as assert from 'assert'
|
||||
import { getDocUri, activate } from './helper'
|
||||
|
||||
describe('Should get diagnostics', () => {
|
||||
const docUri = getDocUri('diagnostics.txt');
|
||||
const docUri = getDocUri('diagnostics.txt')
|
||||
|
||||
it('Diagnoses uppercase texts', async () => {
|
||||
await testDiagnostics(docUri, [
|
||||
{
|
||||
message: 'ANY is all uppercase.',
|
||||
range: toRange(0, 0, 0, 3),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
},
|
||||
{
|
||||
message: 'ANY is all uppercase.',
|
||||
range: toRange(0, 14, 0, 17),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
},
|
||||
{
|
||||
message: 'OS is all uppercase.',
|
||||
range: toRange(0, 18, 0, 20),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
it('Diagnoses uppercase texts', async () => {
|
||||
await testDiagnostics(docUri, [
|
||||
{ message: 'ANY is all uppercase.', range: toRange(0, 0, 0, 3), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' },
|
||||
{ message: 'ANY is all uppercase.', range: toRange(0, 14, 0, 17), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' },
|
||||
{ message: 'OS is all uppercase.', range: toRange(0, 18, 0, 20), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
function toRange(sLine: number, sChar: number, eLine: number, eChar: number) {
|
||||
const start = new vscode.Position(sLine, sChar);
|
||||
const end = new vscode.Position(eLine, eChar);
|
||||
return new vscode.Range(start, end);
|
||||
const start = new vscode.Position(sLine, sChar)
|
||||
const end = new vscode.Position(eLine, eChar)
|
||||
return new vscode.Range(start, end)
|
||||
}
|
||||
|
||||
async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.Diagnostic[]) {
|
||||
await activate(docUri);
|
||||
await activate(docUri)
|
||||
|
||||
const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
|
||||
const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
|
||||
|
||||
assert.equal(actualDiagnostics.length, expectedDiagnostics.length);
|
||||
assert.equal(actualDiagnostics.length, expectedDiagnostics.length);
|
||||
|
||||
expectedDiagnostics.forEach((expectedDiagnostic, i) => {
|
||||
const actualDiagnostic = actualDiagnostics[i];
|
||||
assert.equal(actualDiagnostic.message, expectedDiagnostic.message);
|
||||
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range);
|
||||
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity);
|
||||
});
|
||||
}
|
||||
expectedDiagnostics.forEach((expectedDiagnostic, i) => {
|
||||
const actualDiagnostic = actualDiagnostics[i]
|
||||
assert.equal(actualDiagnostic.message, expectedDiagnostic.message)
|
||||
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range)
|
||||
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity)
|
||||
})
|
||||
}
|
||||
@ -40,6 +40,9 @@ export const getDocUri = (p: string) => {
|
||||
};
|
||||
|
||||
export async function setTestContent(content: string): Promise<boolean> {
|
||||
const all = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length));
|
||||
const all = new vscode.Range(
|
||||
doc.positionAt(0),
|
||||
doc.positionAt(doc.getText().length)
|
||||
);
|
||||
return editor.edit(eb => eb.replace(all, content));
|
||||
}
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
import * as testRunner from 'vscode/lib/testrunner';
|
||||
|
||||
testRunner.configure({
|
||||
ui: 'bdd',
|
||||
useColors: true,
|
||||
timeout: 100000
|
||||
ui: 'bdd',
|
||||
useColors: true,
|
||||
timeout: 100000
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
module.exports = testRunner;
|
||||
@ -35,8 +35,10 @@ connection.onInitialize((params: InitializeParams) => {
|
||||
|
||||
// Does the client support the `workspace/configuration` request?
|
||||
// If not, we will fall back using global settings
|
||||
hasConfigurationCapability = capabilities.workspace && !!capabilities.workspace.configuration;
|
||||
hasWorkspaceFolderCapability = capabilities.workspace && !!capabilities.workspace.workspaceFolders;
|
||||
hasConfigurationCapability =
|
||||
capabilities.workspace && !!capabilities.workspace.configuration;
|
||||
hasWorkspaceFolderCapability =
|
||||
capabilities.workspace && !!capabilities.workspace.workspaceFolders;
|
||||
hasDiagnosticRelatedInformationCapability =
|
||||
capabilities.textDocument &&
|
||||
capabilities.textDocument.publishDiagnostics &&
|
||||
@ -56,7 +58,10 @@ connection.onInitialize((params: InitializeParams) => {
|
||||
connection.onInitialized(() => {
|
||||
if (hasConfigurationCapability) {
|
||||
// Register for all configuration changes.
|
||||
connection.client.register(DidChangeConfigurationNotification.type, undefined);
|
||||
connection.client.register(
|
||||
DidChangeConfigurationNotification.type,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
||||
@ -84,7 +89,9 @@ connection.onDidChangeConfiguration(change => {
|
||||
// Reset all cached document settings
|
||||
documentSettings.clear();
|
||||
} else {
|
||||
globalSettings = <ExampleSettings>(change.settings.languageServerExample || defaultSettings);
|
||||
globalSettings = <ExampleSettings>(
|
||||
(change.settings.languageServerExample || defaultSettings)
|
||||
);
|
||||
}
|
||||
|
||||
// Revalidate all open text documents
|
||||
@ -195,9 +202,11 @@ connection.onCompletion(
|
||||
connection.onCompletionResolve(
|
||||
(item: CompletionItem): CompletionItem => {
|
||||
if (item.data === 1) {
|
||||
(item.detail = 'TypeScript details'), (item.documentation = 'TypeScript documentation');
|
||||
(item.detail = 'TypeScript details'),
|
||||
(item.documentation = 'TypeScript documentation');
|
||||
} else if (item.data === 2) {
|
||||
(item.detail = 'JavaScript details'), (item.documentation = 'JavaScript documentation');
|
||||
(item.detail = 'JavaScript details'),
|
||||
(item.documentation = 'JavaScript documentation');
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import * as path from 'path';
|
||||
import {
|
||||
workspace as Workspace,
|
||||
window as Window,
|
||||
ExtensionContext,
|
||||
TextDocument,
|
||||
OutputChannel,
|
||||
WorkspaceFolder,
|
||||
Uri
|
||||
workspace as Workspace, window as Window, ExtensionContext, TextDocument, OutputChannel, WorkspaceFolder, Uri
|
||||
} from 'vscode';
|
||||
|
||||
import { LanguageClient, LanguageClientOptions, TransportKind } from 'vscode-languageclient';
|
||||
import {
|
||||
LanguageClient, LanguageClientOptions, TransportKind
|
||||
} from 'vscode-languageclient';
|
||||
|
||||
let defaultClient: LanguageClient;
|
||||
let clients: Map<string, LanguageClient> = new Map();
|
||||
@ -17,21 +13,21 @@ let clients: Map<string, LanguageClient> = new Map();
|
||||
let _sortedWorkspaceFolders: string[];
|
||||
function sortedWorkspaceFolders(): string[] {
|
||||
if (_sortedWorkspaceFolders === void 0) {
|
||||
_sortedWorkspaceFolders = Workspace.workspaceFolders
|
||||
.map(folder => {
|
||||
let result = folder.uri.toString();
|
||||
if (result.charAt(result.length - 1) !== '/') {
|
||||
result = result + '/';
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
_sortedWorkspaceFolders = Workspace.workspaceFolders.map(folder => {
|
||||
let result = folder.uri.toString();
|
||||
if (result.charAt(result.length - 1) !== '/') {
|
||||
result = result + '/';
|
||||
}
|
||||
return result;
|
||||
}).sort(
|
||||
(a, b) => {
|
||||
return a.length - b.length;
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
return _sortedWorkspaceFolders;
|
||||
}
|
||||
Workspace.onDidChangeWorkspaceFolders(() => (_sortedWorkspaceFolders = undefined));
|
||||
Workspace.onDidChangeWorkspaceFolders(() => _sortedWorkspaceFolders = undefined);
|
||||
|
||||
function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
|
||||
let sorted = sortedWorkspaceFolders();
|
||||
@ -48,6 +44,7 @@ function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder {
|
||||
}
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
|
||||
let module = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
|
||||
let outputChannel: OutputChannel = Window.createOutputChannel('lsp-multi-server-example');
|
||||
|
||||
@ -60,22 +57,19 @@ export function activate(context: ExtensionContext) {
|
||||
let uri = document.uri;
|
||||
// Untitled files go to a default client.
|
||||
if (uri.scheme === 'untitled' && !defaultClient) {
|
||||
let debugOptions = { execArgv: ['--nolazy', '--inspect=6010'] };
|
||||
let debugOptions = { execArgv: ["--nolazy", "--inspect=6010"] };
|
||||
let serverOptions = {
|
||||
run: { module, transport: TransportKind.ipc },
|
||||
debug: { module, transport: TransportKind.ipc, options: debugOptions }
|
||||
debug: { module, transport: TransportKind.ipc, options: debugOptions}
|
||||
};
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'untitled', language: 'plaintext' }],
|
||||
documentSelector: [
|
||||
{ scheme: 'untitled', language: 'plaintext' }
|
||||
],
|
||||
diagnosticCollectionName: 'lsp-multi-server-example',
|
||||
outputChannel: outputChannel
|
||||
};
|
||||
defaultClient = new LanguageClient(
|
||||
'lsp-multi-server-example',
|
||||
'LSP Multi Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
}
|
||||
defaultClient = new LanguageClient('lsp-multi-server-example', 'LSP Multi Server Example', serverOptions, clientOptions);
|
||||
defaultClient.start();
|
||||
return;
|
||||
}
|
||||
@ -89,23 +83,20 @@ export function activate(context: ExtensionContext) {
|
||||
folder = getOuterMostWorkspaceFolder(folder);
|
||||
|
||||
if (!clients.has(folder.uri.toString())) {
|
||||
let debugOptions = { execArgv: ['--nolazy', `--inspect=${6011 + clients.size}`] };
|
||||
let debugOptions = { execArgv: ["--nolazy", `--inspect=${6011 + clients.size}`] };
|
||||
let serverOptions = {
|
||||
run: { module, transport: TransportKind.ipc },
|
||||
debug: { module, transport: TransportKind.ipc, options: debugOptions }
|
||||
debug: { module, transport: TransportKind.ipc, options: debugOptions}
|
||||
};
|
||||
let clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'plaintext', pattern: `${folder.uri.fsPath}/**/*` }],
|
||||
documentSelector: [
|
||||
{ scheme: 'file', language: 'plaintext', pattern: `${folder.uri.fsPath}/**/*` }
|
||||
],
|
||||
diagnosticCollectionName: 'lsp-multi-server-example',
|
||||
workspaceFolder: folder,
|
||||
outputChannel: outputChannel
|
||||
};
|
||||
let client = new LanguageClient(
|
||||
'lsp-multi-server-example',
|
||||
'LSP Multi Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
}
|
||||
let client = new LanguageClient('lsp-multi-server-example', 'LSP Multi Server Example', serverOptions, clientOptions);
|
||||
client.start();
|
||||
clients.set(folder.uri.toString(), client);
|
||||
}
|
||||
@ -113,8 +104,8 @@ export function activate(context: ExtensionContext) {
|
||||
|
||||
Workspace.onDidOpenTextDocument(didOpenTextDocument);
|
||||
Workspace.textDocuments.forEach(didOpenTextDocument);
|
||||
Workspace.onDidChangeWorkspaceFolders(event => {
|
||||
for (let folder of event.removed) {
|
||||
Workspace.onDidChangeWorkspaceFolders((event) => {
|
||||
for (let folder of event.removed) {
|
||||
let client = clients.get(folder.uri.toString());
|
||||
if (client) {
|
||||
clients.delete(folder.uri.toString());
|
||||
@ -133,4 +124,4 @@ export function deactivate(): Thenable<void> {
|
||||
promises.push(client.stop());
|
||||
}
|
||||
return Promise.all(promises).then(() => undefined);
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,9 @@
|
||||
*--------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { createConnection, TextDocuments, ProposedFeatures, TextDocumentSyncKind } from 'vscode-languageserver';
|
||||
import {
|
||||
createConnection, TextDocuments, ProposedFeatures, TextDocumentSyncKind
|
||||
} from 'vscode-languageserver';
|
||||
|
||||
// Creates the LSP connection
|
||||
let connection = createConnection(ProposedFeatures.all);
|
||||
@ -14,12 +16,12 @@ let documents = new TextDocuments();
|
||||
// The workspace folder this server is operating on
|
||||
let workspaceFolder: string;
|
||||
|
||||
documents.onDidOpen(event => {
|
||||
documents.onDidOpen((event) => {
|
||||
connection.console.log(`[Server(${process.pid}) ${workspaceFolder}] Document opened: ${event.document.uri}`);
|
||||
});
|
||||
})
|
||||
documents.listen(connection);
|
||||
|
||||
connection.onInitialize(params => {
|
||||
connection.onInitialize((params) => {
|
||||
workspaceFolder = params.rootUri;
|
||||
connection.console.log(`[Server(${process.pid}) ${workspaceFolder}] Started and initialize received`);
|
||||
return {
|
||||
@ -29,6 +31,6 @@ connection.onInitialize(params => {
|
||||
change: TextDocumentSyncKind.None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
connection.listen();
|
||||
connection.listen();
|
||||
@ -7,13 +7,20 @@
|
||||
import * as path from 'path';
|
||||
import { workspace, ExtensionContext } from 'vscode';
|
||||
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
|
||||
import {
|
||||
LanguageClient,
|
||||
LanguageClientOptions,
|
||||
ServerOptions,
|
||||
TransportKind
|
||||
} from 'vscode-languageclient';
|
||||
|
||||
let client: LanguageClient;
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
|
||||
let serverModule = context.asAbsolutePath(
|
||||
path.join('server', 'out', 'server.js')
|
||||
);
|
||||
// The debug options for the server
|
||||
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
|
||||
let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
|
||||
@ -40,7 +47,12 @@ export function activate(context: ExtensionContext) {
|
||||
};
|
||||
|
||||
// Create the language client and start the client.
|
||||
client = new LanguageClient('languageServerExample', 'Language Server Example', serverOptions, clientOptions);
|
||||
client = new LanguageClient(
|
||||
'languageServerExample',
|
||||
'Language Server Example',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
|
||||
// Start the client. This will also launch the server
|
||||
client.start();
|
||||
|
||||
@ -4,54 +4,39 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { getDocUri, activate } from './helper';
|
||||
import * as vscode from 'vscode'
|
||||
import * as assert from 'assert'
|
||||
import { getDocUri, activate } from './helper'
|
||||
|
||||
describe('Should get diagnostics', () => {
|
||||
const docUri = getDocUri('diagnostics.txt');
|
||||
const docUri = getDocUri('diagnostics.txt')
|
||||
|
||||
it('Diagnoses uppercase texts', async () => {
|
||||
await testDiagnostics(docUri, [
|
||||
{
|
||||
message: 'ANY is all uppercase.',
|
||||
range: toRange(0, 0, 0, 3),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
},
|
||||
{
|
||||
message: 'ANY is all uppercase.',
|
||||
range: toRange(0, 14, 0, 17),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
},
|
||||
{
|
||||
message: 'OS is all uppercase.',
|
||||
range: toRange(0, 18, 0, 20),
|
||||
severity: vscode.DiagnosticSeverity.Warning,
|
||||
source: 'ex'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
it('Diagnoses uppercase texts', async () => {
|
||||
await testDiagnostics(docUri, [
|
||||
{ message: 'ANY is all uppercase.', range: toRange(0, 0, 0, 3), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' },
|
||||
{ message: 'ANY is all uppercase.', range: toRange(0, 14, 0, 17), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' },
|
||||
{ message: 'OS is all uppercase.', range: toRange(0, 18, 0, 20), severity: vscode.DiagnosticSeverity.Warning, source: 'ex' }
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
function toRange(sLine: number, sChar: number, eLine: number, eChar: number) {
|
||||
const start = new vscode.Position(sLine, sChar);
|
||||
const end = new vscode.Position(eLine, eChar);
|
||||
return new vscode.Range(start, end);
|
||||
const start = new vscode.Position(sLine, sChar)
|
||||
const end = new vscode.Position(eLine, eChar)
|
||||
return new vscode.Range(start, end)
|
||||
}
|
||||
|
||||
async function testDiagnostics(docUri: vscode.Uri, expectedDiagnostics: vscode.Diagnostic[]) {
|
||||
await activate(docUri);
|
||||
await activate(docUri)
|
||||
|
||||
const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
|
||||
const actualDiagnostics = vscode.languages.getDiagnostics(docUri);
|
||||
|
||||
assert.equal(actualDiagnostics.length, expectedDiagnostics.length);
|
||||
assert.equal(actualDiagnostics.length, expectedDiagnostics.length);
|
||||
|
||||
expectedDiagnostics.forEach((expectedDiagnostic, i) => {
|
||||
const actualDiagnostic = actualDiagnostics[i];
|
||||
assert.equal(actualDiagnostic.message, expectedDiagnostic.message);
|
||||
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range);
|
||||
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity);
|
||||
});
|
||||
}
|
||||
expectedDiagnostics.forEach((expectedDiagnostic, i) => {
|
||||
const actualDiagnostic = actualDiagnostics[i]
|
||||
assert.equal(actualDiagnostic.message, expectedDiagnostic.message)
|
||||
assert.deepEqual(actualDiagnostic.range, expectedDiagnostic.range)
|
||||
assert.equal(actualDiagnostic.severity, expectedDiagnostic.severity)
|
||||
})
|
||||
}
|
||||
@ -40,6 +40,9 @@ export const getDocUri = (p: string) => {
|
||||
};
|
||||
|
||||
export async function setTestContent(content: string): Promise<boolean> {
|
||||
const all = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length));
|
||||
const all = new vscode.Range(
|
||||
doc.positionAt(0),
|
||||
doc.positionAt(doc.getText().length)
|
||||
);
|
||||
return editor.edit(eb => eb.replace(all, content));
|
||||
}
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
import * as testRunner from 'vscode/lib/testrunner';
|
||||
|
||||
testRunner.configure({
|
||||
ui: 'bdd',
|
||||
useColors: true,
|
||||
timeout: 100000
|
||||
ui: 'bdd',
|
||||
useColors: true,
|
||||
timeout: 100000
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
module.exports = testRunner;
|
||||
@ -35,8 +35,10 @@ connection.onInitialize((params: InitializeParams) => {
|
||||
|
||||
// Does the client support the `workspace/configuration` request?
|
||||
// If not, we will fall back using global settings
|
||||
hasConfigurationCapability = capabilities.workspace && !!capabilities.workspace.configuration;
|
||||
hasWorkspaceFolderCapability = capabilities.workspace && !!capabilities.workspace.workspaceFolders;
|
||||
hasConfigurationCapability =
|
||||
capabilities.workspace && !!capabilities.workspace.configuration;
|
||||
hasWorkspaceFolderCapability =
|
||||
capabilities.workspace && !!capabilities.workspace.workspaceFolders;
|
||||
hasDiagnosticRelatedInformationCapability =
|
||||
capabilities.textDocument &&
|
||||
capabilities.textDocument.publishDiagnostics &&
|
||||
@ -56,7 +58,10 @@ connection.onInitialize((params: InitializeParams) => {
|
||||
connection.onInitialized(() => {
|
||||
if (hasConfigurationCapability) {
|
||||
// Register for all configuration changes.
|
||||
connection.client.register(DidChangeConfigurationNotification.type, undefined);
|
||||
connection.client.register(
|
||||
DidChangeConfigurationNotification.type,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
if (hasWorkspaceFolderCapability) {
|
||||
connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
||||
@ -84,7 +89,9 @@ connection.onDidChangeConfiguration(change => {
|
||||
// Reset all cached document settings
|
||||
documentSettings.clear();
|
||||
} else {
|
||||
globalSettings = <ExampleSettings>(change.settings.languageServerExample || defaultSettings);
|
||||
globalSettings = <ExampleSettings>(
|
||||
(change.settings.languageServerExample || defaultSettings)
|
||||
);
|
||||
}
|
||||
|
||||
// Revalidate all open text documents
|
||||
@ -195,9 +202,11 @@ connection.onCompletion(
|
||||
connection.onCompletionResolve(
|
||||
(item: CompletionItem): CompletionItem => {
|
||||
if (item.data === 1) {
|
||||
(item.detail = 'TypeScript details'), (item.documentation = 'TypeScript documentation');
|
||||
(item.detail = 'TypeScript details'),
|
||||
(item.documentation = 'TypeScript documentation');
|
||||
} else if (item.data === 2) {
|
||||
(item.detail = 'JavaScript details'), (item.documentation = 'JavaScript documentation');
|
||||
(item.detail = 'JavaScript details'),
|
||||
(item.documentation = 'JavaScript documentation');
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
@ -3,34 +3,32 @@ import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const collection = vscode.languages.createDiagnosticCollection('test');
|
||||
if (vscode.window.activeTextEditor) {
|
||||
updateDiagnostics(vscode.window.activeTextEditor.document, collection);
|
||||
}
|
||||
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(e => updateDiagnostics(e.document, collection)));
|
||||
|
||||
const collection = vscode.languages.createDiagnosticCollection('test');
|
||||
if (vscode.window.activeTextEditor) {
|
||||
updateDiagnostics(vscode.window.activeTextEditor.document, collection);
|
||||
}
|
||||
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(e => updateDiagnostics(e.document, collection)));
|
||||
|
||||
}
|
||||
|
||||
function updateDiagnostics(document: vscode.TextDocument, collection: vscode.DiagnosticCollection): void {
|
||||
if (document && path.basename(document.uri.fsPath) === 'sample-demo.rs') {
|
||||
collection.set(document.uri, [
|
||||
{
|
||||
code: '',
|
||||
message: 'cannot assign twice to immutable variable `x`',
|
||||
range: new vscode.Range(new vscode.Position(3, 4), new vscode.Position(3, 10)),
|
||||
severity: vscode.DiagnosticSeverity.Error,
|
||||
source: '',
|
||||
relatedInformation: [
|
||||
new vscode.DiagnosticRelatedInformation(
|
||||
new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))),
|
||||
'first assignment to `x`'
|
||||
)
|
||||
]
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
collection.clear();
|
||||
}
|
||||
if (document && path.basename(document.uri.fsPath) === 'sample-demo.rs') {
|
||||
collection.set(document.uri, [{
|
||||
code: '',
|
||||
message: 'cannot assign twice to immutable variable `x`',
|
||||
range: new vscode.Range(new vscode.Position(3, 4), new vscode.Position(3, 10)),
|
||||
severity: vscode.DiagnosticSeverity.Error,
|
||||
source: '',
|
||||
relatedInformation: [
|
||||
new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 9))), 'first assignment to `x`')
|
||||
]
|
||||
}]);
|
||||
} else {
|
||||
collection.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
export function deactivate() {
|
||||
}
|
||||
@ -12,10 +12,11 @@ import * as assert from 'assert';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite('Extension Tests', function() {
|
||||
// Defines a Mocha unit test
|
||||
test('Something 1', function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
suite("Extension Tests", function () {
|
||||
|
||||
// Defines a Mocha unit test
|
||||
test("Something 1", function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
@ -15,8 +15,8 @@ import * as testRunner from 'vscode/lib/testrunner';
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
module.exports = testRunner;
|
||||
@ -11,292 +11,265 @@ import * as mkdirp from 'mkdirp';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
vscode.workspace.registerFileSystemProvider('datei', new DateiFileSystemProvider(), {
|
||||
isCaseSensitive: process.platform === 'linux'
|
||||
});
|
||||
vscode.workspace.registerFileSystemProvider('datei', new DateiFileSystemProvider(), {
|
||||
isCaseSensitive: process.platform === 'linux'
|
||||
});
|
||||
}
|
||||
|
||||
class DateiFileSystemProvider implements vscode.FileSystemProvider {
|
||||
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
|
||||
|
||||
constructor() {
|
||||
this._onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
||||
}
|
||||
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
|
||||
|
||||
get onDidChangeFile(): vscode.Event<vscode.FileChangeEvent[]> {
|
||||
return this._onDidChangeFile.event;
|
||||
}
|
||||
constructor() {
|
||||
this._onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
|
||||
}
|
||||
|
||||
watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[] }): vscode.Disposable {
|
||||
const watcher = fs.watch(
|
||||
uri.fsPath,
|
||||
{ recursive: options.recursive },
|
||||
async (event: string, filename: string | Buffer) => {
|
||||
const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString()));
|
||||
get onDidChangeFile(): vscode.Event<vscode.FileChangeEvent[]> {
|
||||
return this._onDidChangeFile.event;
|
||||
}
|
||||
|
||||
// TODO support excludes (using minimatch library?)
|
||||
watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable {
|
||||
const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => {
|
||||
const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString()));
|
||||
|
||||
this._onDidChangeFile.fire([
|
||||
{
|
||||
type:
|
||||
event === 'change'
|
||||
? vscode.FileChangeType.Changed
|
||||
: (await _.exists(filepath))
|
||||
? vscode.FileChangeType.Created
|
||||
: vscode.FileChangeType.Deleted,
|
||||
uri: uri.with({ path: filepath })
|
||||
} as vscode.FileChangeEvent
|
||||
]);
|
||||
}
|
||||
);
|
||||
// TODO support excludes (using minimatch library?)
|
||||
|
||||
return { dispose: () => watcher.close() };
|
||||
}
|
||||
this._onDidChangeFile.fire([{
|
||||
type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted,
|
||||
uri: uri.with({ path: filepath })
|
||||
} as vscode.FileChangeEvent]);
|
||||
});
|
||||
|
||||
stat(uri: vscode.Uri): vscode.FileStat | Thenable<vscode.FileStat> {
|
||||
return this._stat(uri.fsPath);
|
||||
}
|
||||
return { dispose: () => watcher.close() };
|
||||
}
|
||||
|
||||
async _stat(path: string): Promise<vscode.FileStat> {
|
||||
return new FileStat(await _.stat(path));
|
||||
}
|
||||
stat(uri: vscode.Uri): vscode.FileStat | Thenable<vscode.FileStat> {
|
||||
return this._stat(uri.fsPath);
|
||||
}
|
||||
|
||||
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> {
|
||||
return this._readDirectory(uri);
|
||||
}
|
||||
async _stat(path: string): Promise<vscode.FileStat> {
|
||||
return new FileStat(await _.stat(path));
|
||||
}
|
||||
|
||||
async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
const children = await _.readdir(uri.fsPath);
|
||||
readDirectory(uri: vscode.Uri): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> {
|
||||
return this._readDirectory(uri);
|
||||
}
|
||||
|
||||
const result: [string, vscode.FileType][] = [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
const stat = await this._stat(path.join(uri.fsPath, child));
|
||||
result.push([child, stat.type]);
|
||||
}
|
||||
async _readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
|
||||
const children = await _.readdir(uri.fsPath);
|
||||
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
const result: [string, vscode.FileType][] = [];
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
const stat = await this._stat(path.join(uri.fsPath, child));
|
||||
result.push([child, stat.type]);
|
||||
}
|
||||
|
||||
createDirectory(uri: vscode.Uri): void | Thenable<void> {
|
||||
return _.mkdir(uri.fsPath);
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
readFile(uri: vscode.Uri): Uint8Array | Thenable<Uint8Array> {
|
||||
return _.readfile(uri.fsPath);
|
||||
}
|
||||
createDirectory(uri: vscode.Uri): void | Thenable<void> {
|
||||
return _.mkdir(uri.fsPath);
|
||||
}
|
||||
|
||||
writeFile(
|
||||
uri: vscode.Uri,
|
||||
content: Uint8Array,
|
||||
options: { create: boolean; overwrite: boolean }
|
||||
): void | Thenable<void> {
|
||||
return this._writeFile(uri, content, options);
|
||||
}
|
||||
readFile(uri: vscode.Uri): Uint8Array | Thenable<Uint8Array> {
|
||||
return _.readfile(uri.fsPath);
|
||||
}
|
||||
|
||||
async _writeFile(
|
||||
uri: vscode.Uri,
|
||||
content: Uint8Array,
|
||||
options: { create: boolean; overwrite: boolean }
|
||||
): Promise<void> {
|
||||
const exists = await _.exists(uri.fsPath);
|
||||
if (!exists) {
|
||||
if (!options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable<void> {
|
||||
return this._writeFile(uri, content, options);
|
||||
}
|
||||
|
||||
await _.mkdir(path.dirname(uri.fsPath));
|
||||
} else {
|
||||
if (!options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
}
|
||||
}
|
||||
async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> {
|
||||
const exists = await _.exists(uri.fsPath);
|
||||
if (!exists) {
|
||||
if (!options.create) {
|
||||
throw vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
return _.writefile(uri.fsPath, content as Buffer);
|
||||
}
|
||||
await _.mkdir(path.dirname(uri.fsPath));
|
||||
} else {
|
||||
if (!options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
}
|
||||
}
|
||||
|
||||
delete(uri: vscode.Uri, options: { recursive: boolean }): void | Thenable<void> {
|
||||
if (options.recursive) {
|
||||
return _.rmrf(uri.fsPath);
|
||||
}
|
||||
return _.writefile(uri.fsPath, content as Buffer);
|
||||
}
|
||||
|
||||
return _.unlink(uri.fsPath);
|
||||
}
|
||||
delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable<void> {
|
||||
if (options.recursive) {
|
||||
return _.rmrf(uri.fsPath);
|
||||
}
|
||||
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void | Thenable<void> {
|
||||
return this._rename(oldUri, newUri, options);
|
||||
}
|
||||
return _.unlink(uri.fsPath);
|
||||
}
|
||||
|
||||
async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): Promise<void> {
|
||||
const exists = await _.exists(newUri.fsPath);
|
||||
if (exists) {
|
||||
if (!options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
} else {
|
||||
await _.rmrf(newUri.fsPath);
|
||||
}
|
||||
}
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable<void> {
|
||||
return this._rename(oldUri, newUri, options);
|
||||
}
|
||||
|
||||
const parentExists = await _.exists(path.dirname(newUri.fsPath));
|
||||
if (!parentExists) {
|
||||
await _.mkdir(path.dirname(newUri.fsPath));
|
||||
}
|
||||
async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> {
|
||||
const exists = await _.exists(newUri.fsPath);
|
||||
if (exists) {
|
||||
if (!options.overwrite) {
|
||||
throw vscode.FileSystemError.FileExists();
|
||||
} else {
|
||||
await _.rmrf(newUri.fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
return _.rename(oldUri.fsPath, newUri.fsPath);
|
||||
}
|
||||
const parentExists = await _.exists(path.dirname(newUri.fsPath));
|
||||
if (!parentExists) {
|
||||
await _.mkdir(path.dirname(newUri.fsPath));
|
||||
}
|
||||
|
||||
// TODO can implement a fast copy() method with node.js 8.x new fs.copy method
|
||||
return _.rename(oldUri.fsPath, newUri.fsPath);
|
||||
}
|
||||
|
||||
// TODO can implement a fast copy() method with node.js 8.x new fs.copy method
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
export function deactivate() { }
|
||||
|
||||
//#region Utilities
|
||||
|
||||
namespace _ {
|
||||
function handleResult<T>(
|
||||
resolve: (result: T) => void,
|
||||
reject: (error: Error) => void,
|
||||
error: Error | null | undefined,
|
||||
result: T
|
||||
): void {
|
||||
if (error) {
|
||||
reject(massageError(error));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
function massageError(error: Error & { code?: string }): Error {
|
||||
if (error.code === 'ENOENT') {
|
||||
return vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
function handleResult<T>(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void {
|
||||
if (error) {
|
||||
reject(massageError(error));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (error.code === 'EISDIR') {
|
||||
return vscode.FileSystemError.FileIsADirectory();
|
||||
}
|
||||
function massageError(error: Error & { code?: string }): Error {
|
||||
if (error.code === 'ENOENT') {
|
||||
return vscode.FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
if (error.code === 'EEXIST') {
|
||||
return vscode.FileSystemError.FileExists();
|
||||
}
|
||||
if (error.code === 'EISDIR') {
|
||||
return vscode.FileSystemError.FileIsADirectory();
|
||||
}
|
||||
|
||||
if (error.code === 'EPERM' || error.code === 'EACCESS') {
|
||||
return vscode.FileSystemError.NoPermissions();
|
||||
}
|
||||
if (error.code === 'EEXIST') {
|
||||
return vscode.FileSystemError.FileExists();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
if (error.code === 'EPERM' || error.code === 'EACCESS') {
|
||||
return vscode.FileSystemError.NoPermissions();
|
||||
}
|
||||
|
||||
export function checkCancellation(token: vscode.CancellationToken): void {
|
||||
if (token.isCancellationRequested) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
export function normalizeNFC(items: string): string;
|
||||
export function normalizeNFC(items: string[]): string[];
|
||||
export function normalizeNFC(items: string | string[]): string | string[] {
|
||||
if (process.platform !== 'darwin') {
|
||||
return items;
|
||||
}
|
||||
export function checkCancellation(token: vscode.CancellationToken): void {
|
||||
if (token.isCancellationRequested) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(items)) {
|
||||
return items.map(item => item.normalize('NFC'));
|
||||
}
|
||||
export function normalizeNFC(items: string): string;
|
||||
export function normalizeNFC(items: string[]): string[];
|
||||
export function normalizeNFC(items: string | string[]): string | string[] {
|
||||
if (process.platform !== 'darwin') {
|
||||
return items;
|
||||
}
|
||||
|
||||
return items.normalize('NFC');
|
||||
}
|
||||
if (Array.isArray(items)) {
|
||||
return items.map(item => item.normalize('NFC'));
|
||||
}
|
||||
|
||||
export function readdir(path: string): Promise<string[]> {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children)));
|
||||
});
|
||||
}
|
||||
return items.normalize('NFC');
|
||||
}
|
||||
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return new Promise<fs.Stats>((resolve, reject) => {
|
||||
fs.stat(path, (error, stat) => handleResult(resolve, reject, error, stat));
|
||||
});
|
||||
}
|
||||
export function readdir(path: string): Promise<string[]> {
|
||||
return new Promise<string[]>((resolve, reject) => {
|
||||
fs.readdir(path, (error, children) => handleResult(resolve, reject, error, normalizeNFC(children)));
|
||||
});
|
||||
}
|
||||
|
||||
export function readfile(path: string): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer));
|
||||
});
|
||||
}
|
||||
export function stat(path: string): Promise<fs.Stats> {
|
||||
return new Promise<fs.Stats>((resolve, reject) => {
|
||||
fs.stat(path, (error, stat) => handleResult(resolve, reject, error, stat));
|
||||
});
|
||||
}
|
||||
|
||||
export function writefile(path: string, content: Buffer): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
export function readfile(path: string): Promise<Buffer> {
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
fs.readFile(path, (error, buffer) => handleResult(resolve, reject, error, buffer));
|
||||
});
|
||||
}
|
||||
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
fs.exists(path, exists => handleResult(resolve, reject, null, exists));
|
||||
});
|
||||
}
|
||||
export function writefile(path: string, content: Buffer): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.writeFile(path, content, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function rmrf(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
rimraf(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
export function exists(path: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
fs.exists(path, exists => handleResult(resolve, reject, null, exists));
|
||||
});
|
||||
}
|
||||
|
||||
export function mkdir(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
mkdirp(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
export function rmrf(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
rimraf(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
export function mkdir(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
mkdirp(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.unlink(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
export function rename(oldPath: string, newPath: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.rename(oldPath, newPath, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.unlink(path, error => handleResult(resolve, reject, error, void 0));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FileStat implements vscode.FileStat {
|
||||
constructor(private fsStat: fs.Stats) {}
|
||||
|
||||
get type(): vscode.FileType {
|
||||
return this.fsStat.isFile()
|
||||
? vscode.FileType.File
|
||||
: this.fsStat.isDirectory()
|
||||
? vscode.FileType.Directory
|
||||
: this.fsStat.isSymbolicLink()
|
||||
? vscode.FileType.SymbolicLink
|
||||
: vscode.FileType.Unknown;
|
||||
}
|
||||
constructor(private fsStat: fs.Stats) { }
|
||||
|
||||
get isFile(): boolean | undefined {
|
||||
return this.fsStat.isFile();
|
||||
}
|
||||
get type(): vscode.FileType {
|
||||
return this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : this.fsStat.isSymbolicLink() ? vscode.FileType.SymbolicLink : vscode.FileType.Unknown;
|
||||
}
|
||||
|
||||
get isDirectory(): boolean | undefined {
|
||||
return this.fsStat.isDirectory();
|
||||
}
|
||||
get isFile(): boolean | undefined {
|
||||
return this.fsStat.isFile();
|
||||
}
|
||||
|
||||
get isSymbolicLink(): boolean | undefined {
|
||||
return this.fsStat.isSymbolicLink();
|
||||
}
|
||||
get isDirectory(): boolean | undefined {
|
||||
return this.fsStat.isDirectory();
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.fsStat.size;
|
||||
}
|
||||
get isSymbolicLink(): boolean | undefined {
|
||||
return this.fsStat.isSymbolicLink();
|
||||
}
|
||||
|
||||
get ctime(): number {
|
||||
return this.fsStat.ctime.getTime();
|
||||
}
|
||||
get size(): number {
|
||||
return this.fsStat.size;
|
||||
}
|
||||
|
||||
get mtime(): number {
|
||||
return this.fsStat.mtime.getTime();
|
||||
}
|
||||
get ctime(): number {
|
||||
return this.fsStat.ctime.getTime();
|
||||
}
|
||||
|
||||
get mtime(): number {
|
||||
return this.fsStat.mtime.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#endregion
|
||||
@ -12,10 +12,11 @@ import * as assert from 'assert';
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
// Defines a Mocha test suite to group tests of similar kind together
|
||||
suite('Extension Tests', function() {
|
||||
// Defines a Mocha unit test
|
||||
test('Something 1', function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
suite("Extension Tests", function () {
|
||||
|
||||
// Defines a Mocha unit test
|
||||
test("Something 1", function() {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
@ -15,8 +15,8 @@ import * as testRunner from 'vscode/lib/testrunner';
|
||||
// You can directly control Mocha options by uncommenting the following lines
|
||||
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
|
||||
testRunner.configure({
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
|
||||
useColors: true // colored output from test results
|
||||
});
|
||||
|
||||
module.exports = testRunner;
|
||||
module.exports = testRunner;
|
||||
@ -4,55 +4,40 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
ExtensionContext,
|
||||
StatusBarAlignment,
|
||||
window,
|
||||
StatusBarItem,
|
||||
Selection,
|
||||
workspace,
|
||||
TextEditor,
|
||||
commands,
|
||||
ProgressLocation
|
||||
} from 'vscode';
|
||||
import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands, ProgressLocation } from 'vscode';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand('extension.startTask', () => {
|
||||
window.withProgress(
|
||||
{
|
||||
location: ProgressLocation.Notification,
|
||||
title: 'I am long running!',
|
||||
cancellable: true
|
||||
},
|
||||
(progress, token) => {
|
||||
token.onCancellationRequested(() => {
|
||||
console.log('User canceled the long running operation');
|
||||
});
|
||||
context.subscriptions.push(commands.registerCommand('extension.startTask', () => {
|
||||
window.withProgress({
|
||||
location: ProgressLocation.Notification,
|
||||
title: "I am long running!",
|
||||
cancellable: true
|
||||
}, (progress, token) => {
|
||||
token.onCancellationRequested(() => {
|
||||
console.log("User canceled the long running operation")
|
||||
});
|
||||
|
||||
progress.report({ increment: 0 });
|
||||
progress.report({ increment: 0 });
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 10, message: 'I am long running! - still going...' });
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 10, message: "I am long running! - still going..." });
|
||||
}, 1000);
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 40, message: 'I am long running! - still going even more...' });
|
||||
}, 2000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 40, message: "I am long running! - still going even more..." });
|
||||
}, 2000);
|
||||
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 50, message: 'I am long running! - almost there...' });
|
||||
}, 3000);
|
||||
setTimeout(() => {
|
||||
progress.report({ increment: 50, message: "I am long running! - almost there..." });
|
||||
}, 3000);
|
||||
|
||||
var p = new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 5000);
|
||||
});
|
||||
var p = new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
return p;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@ -11,23 +11,22 @@ import { multiStepInput } from './multiStepInput';
|
||||
import { quickOpen } from './quickOpen';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand('samples.quickInput', async () => {
|
||||
const options: { [key: string]: (context: ExtensionContext) => Promise<void> } = {
|
||||
showQuickPick,
|
||||
showInputBox,
|
||||
multiStepInput,
|
||||
quickOpen
|
||||
};
|
||||
const quickPick = window.createQuickPick();
|
||||
quickPick.items = Object.keys(options).map(label => ({ label }));
|
||||
quickPick.onDidChangeSelection(selection => {
|
||||
if (selection[0]) {
|
||||
options[selection[0].label](context).catch(console.error);
|
||||
}
|
||||
});
|
||||
quickPick.onDidHide(() => quickPick.dispose());
|
||||
quickPick.show();
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(commands.registerCommand('samples.quickInput', async () => {
|
||||
const options: { [key: string]: (context: ExtensionContext) => Promise<void> } = {
|
||||
showQuickPick,
|
||||
showInputBox,
|
||||
multiStepInput,
|
||||
quickOpen,
|
||||
};
|
||||
const quickPick = window.createQuickPick();
|
||||
quickPick.items = Object.keys(options).map(label => ({ label }));
|
||||
quickPick.onDidChangeSelection(selection => {
|
||||
if (selection[0]) {
|
||||
options[selection[0].label](context)
|
||||
.catch(console.error);
|
||||
}
|
||||
});
|
||||
quickPick.onDidHide(() => quickPick.dispose());
|
||||
quickPick.show();
|
||||
}));
|
||||
}
|
||||
|
||||
@ -3,43 +3,27 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import {
|
||||
QuickPickItem,
|
||||
window,
|
||||
Disposable,
|
||||
CancellationToken,
|
||||
QuickInputButton,
|
||||
QuickInput,
|
||||
ExtensionContext,
|
||||
QuickInputButtons,
|
||||
Uri
|
||||
} from 'vscode';
|
||||
import { QuickPickItem, window, Disposable, CancellationToken, QuickInputButton, QuickInput, ExtensionContext, QuickInputButtons, Uri } from 'vscode';
|
||||
|
||||
/**
|
||||
* A multi-step input using window.createQuickPick() and window.createInputBox().
|
||||
*
|
||||
*
|
||||
* This first part uses the helper class `MultiStepInput` that wraps the API for the multi-step case.
|
||||
*/
|
||||
export async function multiStepInput(context: ExtensionContext) {
|
||||
|
||||
class MyButton implements QuickInputButton {
|
||||
constructor(public iconPath: { light: Uri; dark: Uri }, public tooltip: string) {}
|
||||
constructor(public iconPath: { light: Uri; dark: Uri; }, public tooltip: string) { }
|
||||
}
|
||||
|
||||
const createResourceGroupButton = new MyButton(
|
||||
{
|
||||
dark: Uri.file(context.asAbsolutePath('resources/dark/add.svg')),
|
||||
light: Uri.file(context.asAbsolutePath('resources/light/add.svg'))
|
||||
},
|
||||
'Create Resource Group'
|
||||
);
|
||||
const createResourceGroupButton = new MyButton({
|
||||
dark: Uri.file(context.asAbsolutePath('resources/dark/add.svg')),
|
||||
light: Uri.file(context.asAbsolutePath('resources/light/add.svg')),
|
||||
}, 'Create Resource Group');
|
||||
|
||||
const resourceGroups: QuickPickItem[] = ['vscode-data-function', 'vscode-appservice-microservices', 'vscode-appservice-monitor', 'vscode-appservice-preview', 'vscode-appservice-prod']
|
||||
.map(label => ({ label }));
|
||||
|
||||
const resourceGroups: QuickPickItem[] = [
|
||||
'vscode-data-function',
|
||||
'vscode-appservice-microservices',
|
||||
'vscode-appservice-monitor',
|
||||
'vscode-appservice-preview',
|
||||
'vscode-appservice-prod'
|
||||
].map(label => ({ label }));
|
||||
|
||||
interface State {
|
||||
title: string;
|
||||
@ -121,7 +105,9 @@ export async function multiStepInput(context: ExtensionContext) {
|
||||
|
||||
function shouldResume() {
|
||||
// Could show a notification with the option to resume.
|
||||
return new Promise<boolean>((resolve, reject) => {});
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
async function validateNameIsUnique(name: string) {
|
||||
@ -130,25 +116,25 @@ export async function multiStepInput(context: ExtensionContext) {
|
||||
return name === 'vscode' ? 'Name not unique' : undefined;
|
||||
}
|
||||
|
||||
async function getAvailableRuntimes(
|
||||
resourceGroup: QuickPickItem | string,
|
||||
token?: CancellationToken
|
||||
): Promise<QuickPickItem[]> {
|
||||
async function getAvailableRuntimes(resourceGroup: QuickPickItem | string, token?: CancellationToken): Promise<QuickPickItem[]> {
|
||||
// ...retrieve...
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return ['Node 8.9', 'Node 6.11', 'Node 4.5'].map(label => ({ label }));
|
||||
return ['Node 8.9', 'Node 6.11', 'Node 4.5']
|
||||
.map(label => ({ label }));
|
||||
}
|
||||
|
||||
const state = await collectInputs();
|
||||
window.showInformationMessage(`Creating Application Service '${state.name}'`);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Helper code that wraps the API for the multi-step case.
|
||||
// -------------------------------------------------------
|
||||
|
||||
|
||||
class InputFlowAction {
|
||||
private constructor() {}
|
||||
private constructor() { }
|
||||
static back = new InputFlowAction();
|
||||
static cancel = new InputFlowAction();
|
||||
static resume = new InputFlowAction();
|
||||
@ -179,6 +165,7 @@ interface InputBoxParameters {
|
||||
}
|
||||
|
||||
class MultiStepInput {
|
||||
|
||||
static async run<T>(start: InputStep) {
|
||||
const input = new MultiStepInput();
|
||||
return input.stepThrough(start);
|
||||
@ -215,16 +202,7 @@ class MultiStepInput {
|
||||
}
|
||||
}
|
||||
|
||||
async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>({
|
||||
title,
|
||||
step,
|
||||
totalSteps,
|
||||
items,
|
||||
activeItem,
|
||||
placeholder,
|
||||
buttons,
|
||||
shouldResume
|
||||
}: P) {
|
||||
async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>({ title, step, totalSteps, items, activeItem, placeholder, buttons, shouldResume }: P) {
|
||||
const disposables: Disposable[] = [];
|
||||
try {
|
||||
return await new Promise<T | (P extends { buttons: (infer I)[] } ? I : never)>((resolve, reject) => {
|
||||
@ -237,7 +215,10 @@ class MultiStepInput {
|
||||
if (activeItem) {
|
||||
input.activeItems = [activeItem];
|
||||
}
|
||||
input.buttons = [...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), ...(buttons || [])];
|
||||
input.buttons = [
|
||||
...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
|
||||
...(buttons || [])
|
||||
];
|
||||
disposables.push(
|
||||
input.onDidTriggerButton(item => {
|
||||
if (item === QuickInputButtons.Back) {
|
||||
@ -249,8 +230,9 @@ class MultiStepInput {
|
||||
input.onDidChangeSelection(items => resolve(items[0])),
|
||||
input.onDidHide(() => {
|
||||
(async () => {
|
||||
reject(shouldResume && (await shouldResume()) ? InputFlowAction.resume : InputFlowAction.cancel);
|
||||
})().catch(reject);
|
||||
reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
|
||||
})()
|
||||
.catch(reject);
|
||||
})
|
||||
);
|
||||
if (this.current) {
|
||||
@ -264,16 +246,7 @@ class MultiStepInput {
|
||||
}
|
||||
}
|
||||
|
||||
async showInputBox<P extends InputBoxParameters>({
|
||||
title,
|
||||
step,
|
||||
totalSteps,
|
||||
value,
|
||||
prompt,
|
||||
validate,
|
||||
buttons,
|
||||
shouldResume
|
||||
}: P) {
|
||||
async showInputBox<P extends InputBoxParameters>({ title, step, totalSteps, value, prompt, validate, buttons, shouldResume }: P) {
|
||||
const disposables: Disposable[] = [];
|
||||
try {
|
||||
return await new Promise<string | (P extends { buttons: (infer I)[] } ? I : never)>((resolve, reject) => {
|
||||
@ -283,7 +256,10 @@ class MultiStepInput {
|
||||
input.totalSteps = totalSteps;
|
||||
input.value = value || '';
|
||||
input.prompt = prompt;
|
||||
input.buttons = [...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), ...(buttons || [])];
|
||||
input.buttons = [
|
||||
...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
|
||||
...(buttons || [])
|
||||
];
|
||||
let validating = validate('');
|
||||
disposables.push(
|
||||
input.onDidTriggerButton(item => {
|
||||
@ -313,8 +289,9 @@ class MultiStepInput {
|
||||
}),
|
||||
input.onDidHide(() => {
|
||||
(async () => {
|
||||
reject(shouldResume && (await shouldResume()) ? InputFlowAction.resume : InputFlowAction.cancel);
|
||||
})().catch(reject);
|
||||
reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
|
||||
})()
|
||||
.catch(reject);
|
||||
})
|
||||
);
|
||||
if (this.current) {
|
||||
|
||||
@ -11,7 +11,7 @@ import { workspace } from 'vscode';
|
||||
|
||||
/**
|
||||
* A file opener using window.createQuickPick().
|
||||
*
|
||||
*
|
||||
* It shows how the list of items can be dynamically updated based on
|
||||
* the user's input in the filter field.
|
||||
*/
|
||||
@ -24,9 +24,10 @@ export async function quickOpen() {
|
||||
}
|
||||
|
||||
class FileItem implements QuickPickItem {
|
||||
|
||||
label: string;
|
||||
description: string;
|
||||
|
||||
|
||||
constructor(public base: Uri, public uri: Uri) {
|
||||
this.label = path.basename(uri.fsPath);
|
||||
this.description = path.dirname(path.relative(base.fsPath, uri.fsPath));
|
||||
@ -34,10 +35,11 @@ class FileItem implements QuickPickItem {
|
||||
}
|
||||
|
||||
class MessageItem implements QuickPickItem {
|
||||
|
||||
label: string;
|
||||
description = '';
|
||||
detail: string;
|
||||
|
||||
|
||||
constructor(public base: Uri, public message: string) {
|
||||
this.label = message.replace(/\r?\n/g, ' ');
|
||||
this.detail = base.fsPath;
|
||||
@ -60,7 +62,7 @@ async function pickFile() {
|
||||
}
|
||||
input.busy = true;
|
||||
const cwds = workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.fsPath) : [process.cwd()];
|
||||
const q = process.platform === 'win32' ? '"' : "'";
|
||||
const q = process.platform === 'win32' ? '"' : '\'';
|
||||
rgs = cwds.map(cwd => {
|
||||
const rg = cp.exec(`rg --files -g ${q}*${value}*${q}`, { cwd }, (err, stdout) => {
|
||||
const i = rgs.indexOf(rg);
|
||||
@ -71,13 +73,14 @@ async function pickFile() {
|
||||
if (!err) {
|
||||
input.items = input.items.concat(
|
||||
stdout
|
||||
.split('\n')
|
||||
.slice(0, 50)
|
||||
.split('\n').slice(0, 50)
|
||||
.map(relative => new FileItem(Uri.file(cwd), Uri.file(path.join(cwd, relative))))
|
||||
);
|
||||
}
|
||||
if (err && !(<any>err).killed && (<any>err).code !== 1 && err.message) {
|
||||
input.items = input.items.concat([new MessageItem(Uri.file(cwd), err.message)]);
|
||||
input.items = input.items.concat([
|
||||
new MessageItem(Uri.file(cwd), err.message)
|
||||
]);
|
||||
}
|
||||
rgs.splice(i, 1);
|
||||
if (!rgs.length) {
|
||||
|
||||
@ -6,47 +6,44 @@ import * as html from 'vscode-html-languageservice';
|
||||
import { TextDocument, Position } from 'vscode-languageserver-types';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// create and keep html language service
|
||||
const service = html.getLanguageService();
|
||||
|
||||
vscode.languages.registerCompletionItemProvider(['typescript', 'javascript'], {
|
||||
provideCompletionItems(doc, pos) {
|
||||
const offset = doc.offsetAt(pos);
|
||||
const source = ts.createSourceFile(doc.fileName, doc.getText(), ts.ScriptTarget.Latest, true);
|
||||
// create and keep html language service
|
||||
const service = html.getLanguageService();
|
||||
|
||||
let token = (ts as any).getTokenAtPosition(source, offset);
|
||||
let template: ts.TaggedTemplateExpression;
|
||||
while (token) {
|
||||
if (token.kind === ts.SyntaxKind.TaggedTemplateExpression) {
|
||||
template = token;
|
||||
break;
|
||||
}
|
||||
token = token.parent;
|
||||
}
|
||||
vscode.languages.registerCompletionItemProvider(['typescript', 'javascript'], {
|
||||
provideCompletionItems(doc, pos) {
|
||||
|
||||
if (
|
||||
!template ||
|
||||
template.tag.getText() !== 'html' ||
|
||||
(offset < template.template.pos && offset > template.template.end)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const offset = doc.offsetAt(pos);
|
||||
const source = ts.createSourceFile(doc.fileName, doc.getText(), ts.ScriptTarget.Latest, true);
|
||||
|
||||
const content = template.template.getText().slice(1, -1);
|
||||
const embeddedDoc = TextDocument.create(
|
||||
doc.uri.with({ scheme: 'html-fake' }).toString(),
|
||||
'html',
|
||||
doc.version,
|
||||
content
|
||||
);
|
||||
const htmlDoc = service.parseHTMLDocument(embeddedDoc);
|
||||
let token = (ts as any).getTokenAtPosition(source, offset)
|
||||
let template: ts.TaggedTemplateExpression;
|
||||
while (token) {
|
||||
if (token.kind === ts.SyntaxKind.TaggedTemplateExpression) {
|
||||
template = token;
|
||||
break;
|
||||
}
|
||||
token = token.parent;
|
||||
}
|
||||
|
||||
const list = service.doComplete(embeddedDoc, Position.create(0, offset - template.template.pos - 1), htmlDoc);
|
||||
if (!template
|
||||
|| template.tag.getText() !== 'html'
|
||||
|| (offset < template.template.pos && offset > template.template.end)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
return list.items.map(item => {
|
||||
// translate to vscode items
|
||||
return new vscode.CompletionItem(item.label);
|
||||
});
|
||||
}
|
||||
});
|
||||
const content = template.template.getText().slice(1, -1);
|
||||
const embeddedDoc = TextDocument.create(doc.uri.with({ scheme: 'html-fake' }).toString(), 'html', doc.version, content);
|
||||
const htmlDoc = service.parseHTMLDocument(embeddedDoc);
|
||||
|
||||
const list = service.doComplete(embeddedDoc, Position.create(0, offset - template.template.pos - 1), htmlDoc);
|
||||
|
||||
return list.items.map(item => {
|
||||
// translate to vscode items
|
||||
return new vscode.CompletionItem(item.label);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -4,16 +4,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
ExtensionContext,
|
||||
StatusBarAlignment,
|
||||
window,
|
||||
StatusBarItem,
|
||||
Selection,
|
||||
workspace,
|
||||
TextEditor,
|
||||
commands
|
||||
} from 'vscode';
|
||||
import { ExtensionContext, StatusBarAlignment, window, StatusBarItem, Selection, workspace, TextEditor, commands } from 'vscode';
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
const status = window.createStatusBarItem(StatusBarAlignment.Right, 100);
|
||||
@ -26,11 +17,9 @@ export function activate(context: ExtensionContext) {
|
||||
context.subscriptions.push(workspace.onDidOpenTextDocument(e => updateStatus(status)));
|
||||
context.subscriptions.push(workspace.onDidCloseTextDocument(e => updateStatus(status)));
|
||||
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand('extension.selectedLines', () => {
|
||||
window.showInformationMessage(getSelectedLines());
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(commands.registerCommand('extension.selectedLines', () => {
|
||||
window.showInformationMessage(getSelectedLines());
|
||||
}));
|
||||
|
||||
updateStatus(status);
|
||||
}
|
||||
@ -55,7 +44,7 @@ function getSelectedLines(): string {
|
||||
if (editor) {
|
||||
let lines = 0;
|
||||
editor.selections.forEach(selection => {
|
||||
lines += selection.end.line - selection.start.line + 1;
|
||||
lines += (selection.end.line - selection.start.line + 1);
|
||||
});
|
||||
|
||||
if (lines > 0) {
|
||||
|
||||
@ -19,9 +19,9 @@ export function activate(_context: vscode.ExtensionContext): void {
|
||||
let pattern = path.join(workspaceRoot, 'Rakefile');
|
||||
let rakePromise: Thenable<vscode.Task[]> | undefined = undefined;
|
||||
let fileWatcher = vscode.workspace.createFileSystemWatcher(pattern);
|
||||
fileWatcher.onDidChange(() => (rakePromise = undefined));
|
||||
fileWatcher.onDidCreate(() => (rakePromise = undefined));
|
||||
fileWatcher.onDidDelete(() => (rakePromise = undefined));
|
||||
fileWatcher.onDidChange(() => rakePromise = undefined);
|
||||
fileWatcher.onDidCreate(() => rakePromise = undefined);
|
||||
fileWatcher.onDidDelete(() => rakePromise = undefined);
|
||||
taskProvider = vscode.tasks.registerTaskProvider('rake', {
|
||||
provideTasks: () => {
|
||||
if (!rakePromise) {
|
||||
@ -43,7 +43,7 @@ export function deactivate(): void {
|
||||
|
||||
function exists(file: string): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, _reject) => {
|
||||
fs.exists(file, value => {
|
||||
fs.exists(file, (value) => {
|
||||
resolve(value);
|
||||
});
|
||||
});
|
||||
@ -107,7 +107,7 @@ async function getRakeTasks(): Promise<vscode.Task[]> {
|
||||
return emptyTasks;
|
||||
}
|
||||
let rakeFile = path.join(workspaceRoot, 'Rakefile');
|
||||
if (!(await exists(rakeFile))) {
|
||||
if (!await exists(rakeFile)) {
|
||||
return emptyTasks;
|
||||
}
|
||||
|
||||
|
||||
@ -7,13 +7,13 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
vscode.window.showInformationMessage('Hello World!');
|
||||
|
||||
console.log('Terminals: ' + (<any>vscode.window).terminals.length);
|
||||
console.log("Terminals: " + (<any>vscode.window).terminals.length);
|
||||
|
||||
(<any>vscode.window).onDidOpenTerminal(e => {
|
||||
console.log('Terminal opened. Total count: ' + (<any>vscode.window).terminals.length);
|
||||
console.log("Terminal opened. Total count: " + (<any>vscode.window).terminals.length);
|
||||
|
||||
e.onDidWriteData(data => {
|
||||
console.log('Terminal data: ', data);
|
||||
console.log("Terminal data: ", data);
|
||||
});
|
||||
});
|
||||
|
||||
@ -22,106 +22,86 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
});
|
||||
|
||||
// vscode.window.createTerminal
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.createTerminal', () => {
|
||||
vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
|
||||
vscode.window.showInformationMessage('Hello World 2!');
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.createAndSend', () => {
|
||||
const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
|
||||
terminal.sendText("echo 'Sent text immediately after creating'");
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.createZshLoginShell', () => {
|
||||
vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`, '/bin/zsh', ['-l']);
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createTerminal', () => {
|
||||
vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
|
||||
vscode.window.showInformationMessage('Hello World 2!');
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {
|
||||
const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
|
||||
terminal.sendText("echo 'Sent text immediately after creating'");
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createZshLoginShell', () => {
|
||||
vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`, '/bin/zsh', ['-l']);
|
||||
}));
|
||||
|
||||
// Terminal.hide
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.hide', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.hide());
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.hide', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.hide());
|
||||
}
|
||||
}));
|
||||
|
||||
// Terminal.show
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.show', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.show());
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.showPreserveFocus', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.show(true));
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.show', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.show());
|
||||
}
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.showPreserveFocus', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.show(true));
|
||||
}
|
||||
}));
|
||||
|
||||
// Terminal.sendText
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.sendText', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.sendText("echo 'Hello world!'"));
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.sendTextNoNewLine', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.sendText("echo 'Hello world!'", false));
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendText', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.sendText("echo 'Hello world!'"));
|
||||
}
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.sendTextNoNewLine', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => terminal.sendText("echo 'Hello world!'", false));
|
||||
}
|
||||
}));
|
||||
|
||||
// Terminal.dispose
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.dispose', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => {
|
||||
if (terminal) {
|
||||
terminal.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.dispose', () => {
|
||||
if (ensureTerminalExists()) {
|
||||
selectTerminal().then(terminal => {
|
||||
if (terminal) {
|
||||
terminal.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
// Terminal.processId
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.processId', () => {
|
||||
selectTerminal().then(terminal => {
|
||||
terminal.processId.then(processId => {
|
||||
if (processId) {
|
||||
vscode.window.showInformationMessage(`Terminal.processId: ${processId}`);
|
||||
} else {
|
||||
vscode.window.showInformationMessage('Terminal does not have a process ID');
|
||||
}
|
||||
});
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.processId', () => {
|
||||
selectTerminal().then(terminal => {
|
||||
terminal.processId.then((processId) => {
|
||||
if (processId) {
|
||||
vscode.window.showInformationMessage(`Terminal.processId: ${processId}`);
|
||||
} else {
|
||||
vscode.window.showInformationMessage('Terminal does not have a process ID');
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
// vscode.window.onDidCloseTerminal
|
||||
vscode.window.onDidCloseTerminal(terminal => {
|
||||
vscode.window.onDidCloseTerminal((terminal) => {
|
||||
vscode.window.showInformationMessage(`onDidCloseTerminal, name: ${terminal.name}`);
|
||||
});
|
||||
|
||||
|
||||
// vvv Proposed APIs in 1.23 below vvv
|
||||
|
||||
|
||||
// vscode.window.terminals
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.terminals', () => {
|
||||
selectTerminal();
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminals', () => {
|
||||
selectTerminal();
|
||||
}));
|
||||
|
||||
// vscode.window.onDidOpenTerminal
|
||||
if ('onDidOpenTerminal' in vscode.window) {
|
||||
@ -129,98 +109,80 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
vscode.window.showInformationMessage(`onDidOpenTerminal, name: ${terminal.name}`);
|
||||
});
|
||||
}
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.onDidWriteData', () => {
|
||||
selectTerminal().then(terminal => {
|
||||
vscode.window.showInformationMessage(
|
||||
`onDidWriteData listener attached for terminal: ${terminal.name}, check the devtools console to see events`
|
||||
);
|
||||
(<any>terminal).onDidWriteData((data: string) => {
|
||||
console.log('onDidWriteData: ' + data);
|
||||
});
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.onDidWriteData', () => {
|
||||
selectTerminal().then(terminal => {
|
||||
vscode.window.showInformationMessage(`onDidWriteData listener attached for terminal: ${terminal.name}, check the devtools console to see events`);
|
||||
(<any>terminal).onDidWriteData((data: string) => {
|
||||
console.log('onDidWriteData: ' + data);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
}));
|
||||
|
||||
// vvv Proposed APIs in 1.25 below vvv
|
||||
let renderer;
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.terminalRendererCreate', () => {
|
||||
renderer = (<any>vscode.window).createTerminalRenderer('renderer');
|
||||
renderer.write(colorText('~~~ Hello world! ~~~'));
|
||||
renderer.onDidChangeMaximumDimensions(dim => {
|
||||
console.log(`Dimensions for renderer changed: columns=${dim.columns}, rows=${dim.rows}`);
|
||||
});
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.terminalRendererName', () => {
|
||||
if (!renderer) {
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminalRendererCreate', () => {
|
||||
renderer = (<any>vscode.window).createTerminalRenderer('renderer');
|
||||
renderer.write(colorText('~~~ Hello world! ~~~'));
|
||||
renderer.onDidChangeMaximumDimensions(dim => {
|
||||
console.log(`Dimensions for renderer changed: columns=${dim.columns}, rows=${dim.rows}`);
|
||||
});
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminalRendererName', () => {
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: "Enter a new name" }).then(value => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter a new name' }).then(value => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
renderer.name = value;
|
||||
});
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.terminalRendererWrite', () => {
|
||||
if (!renderer) {
|
||||
renderer.name = value;
|
||||
});
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.terminalRendererWrite', () => {
|
||||
if (!renderer) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: "Enter text to write" }).then(value => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter text to write' }).then(value => {
|
||||
if (!value) {
|
||||
// Note that entering characters like `\r` in the input box will result in `\\r` being written
|
||||
renderer.write(value);
|
||||
});
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createFakeShell', () => {
|
||||
const shell = (<any>vscode.window).createTerminalRenderer('fake shell');
|
||||
shell.write('Type and press enter to echo the text\r\n\r\n');
|
||||
let line = '';
|
||||
shell.onDidAcceptInput(data => {
|
||||
if (data === '\r') {
|
||||
shell.write(`\r\necho: "${colorText(line)}"\r\n\n`);
|
||||
line = '';
|
||||
return;
|
||||
}
|
||||
line += data;
|
||||
shell.write(data);
|
||||
});
|
||||
shell.terminal.show();
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.maximumDimensions', () => {
|
||||
renderer.maximumDimensions.then(dimensions => {
|
||||
vscode.window.showInformationMessage(`TerminalRenderer.maximumDimensions: columns=${dimensions.columns}, rows=${dimensions.rows}`);
|
||||
});
|
||||
}));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.dimensions', () => {
|
||||
vscode.window.showInputBox({ placeHolder: "Enter columns" }).then(columns => {
|
||||
if (!columns) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: "Enter rows" }).then(rows => {
|
||||
if (!rows) {
|
||||
return;
|
||||
}
|
||||
// Note that entering characters like `\r` in the input box will result in `\\r` being written
|
||||
renderer.write(value);
|
||||
renderer.dimensions = { columns: parseInt(columns, 10), rows: parseInt(rows, 10) };
|
||||
});
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.createFakeShell', () => {
|
||||
const shell = (<any>vscode.window).createTerminalRenderer('fake shell');
|
||||
shell.write('Type and press enter to echo the text\r\n\r\n');
|
||||
let line = '';
|
||||
shell.onDidAcceptInput(data => {
|
||||
if (data === '\r') {
|
||||
shell.write(`\r\necho: "${colorText(line)}"\r\n\n`);
|
||||
line = '';
|
||||
return;
|
||||
}
|
||||
line += data;
|
||||
shell.write(data);
|
||||
});
|
||||
shell.terminal.show();
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.maximumDimensions', () => {
|
||||
renderer.maximumDimensions.then(dimensions => {
|
||||
vscode.window.showInformationMessage(
|
||||
`TerminalRenderer.maximumDimensions: columns=${dimensions.columns}, rows=${dimensions.rows}`
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('terminalTest.dimensions', () => {
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter columns' }).then(columns => {
|
||||
if (!columns) {
|
||||
return;
|
||||
}
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter rows' }).then(rows => {
|
||||
if (!rows) {
|
||||
return;
|
||||
}
|
||||
renderer.dimensions = { columns: parseInt(columns, 10), rows: parseInt(rows, 10) };
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function colorText(text: string): string {
|
||||
@ -250,7 +212,7 @@ function selectTerminal(): Thenable<vscode.Terminal> {
|
||||
label: `name: ${t.name}`,
|
||||
terminal: t
|
||||
};
|
||||
});
|
||||
})
|
||||
return vscode.window.showQuickPick(items).then(item => {
|
||||
return item.terminal;
|
||||
});
|
||||
@ -262,4 +224,4 @@ function ensureTerminalExists(): boolean {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { DepNodeProvider } from './nodeDependencies';
|
||||
import { JsonOutlineProvider } from './jsonOutline';
|
||||
import { FtpExplorer } from './ftpExplorer.textDocumentContentProvider';
|
||||
import { DepNodeProvider } from './nodeDependencies'
|
||||
import { JsonOutlineProvider } from './jsonOutline'
|
||||
import { FtpExplorer } from './ftpExplorer.textDocumentContentProvider'
|
||||
import { FileExplorer } from './fileExplorer';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
@ -19,19 +19,14 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider);
|
||||
vscode.commands.registerCommand('nodeDependencies.refreshEntry', () => nodeDependenciesProvider.refresh());
|
||||
vscode.commands.registerCommand('nodeDependencies.addEntry', node =>
|
||||
vscode.window.showInformationMessage('Successfully called add entry')
|
||||
);
|
||||
vscode.commands.registerCommand('nodeDependencies.deleteEntry', node =>
|
||||
vscode.window.showInformationMessage('Successfully called delete entry')
|
||||
);
|
||||
vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName =>
|
||||
vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`))
|
||||
);
|
||||
vscode.commands.registerCommand('nodeDependencies.addEntry', node => vscode.window.showInformationMessage('Successfully called add entry'));
|
||||
vscode.commands.registerCommand('nodeDependencies.deleteEntry', node => vscode.window.showInformationMessage('Successfully called delete entry'));
|
||||
vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`)));
|
||||
|
||||
vscode.window.registerTreeDataProvider('jsonOutline', jsonOutlineProvider);
|
||||
vscode.commands.registerCommand('jsonOutline.refresh', () => jsonOutlineProvider.refresh());
|
||||
vscode.commands.registerCommand('jsonOutline.refreshNode', offset => jsonOutlineProvider.refresh(offset));
|
||||
vscode.commands.registerCommand('jsonOutline.renameNode', offset => jsonOutlineProvider.rename(offset));
|
||||
vscode.commands.registerCommand('extension.openJsonSelection', range => jsonOutlineProvider.select(range));
|
||||
|
||||
}
|
||||
|
||||
@ -7,12 +7,8 @@ import * as rimraf from 'rimraf';
|
||||
//#region Utilities
|
||||
|
||||
namespace _ {
|
||||
function handleResult<T>(
|
||||
resolve: (result: T) => void,
|
||||
reject: (error: Error) => void,
|
||||
error: Error | null | undefined,
|
||||
result: T
|
||||
): void {
|
||||
|
||||
function handleResult<T>(resolve: (result: T) => void, reject: (error: Error) => void, error: Error | null | undefined, result: T): void {
|
||||
if (error) {
|
||||
reject(massageError(error));
|
||||
} else {
|
||||
@ -116,16 +112,11 @@ namespace _ {
|
||||
}
|
||||
|
||||
export class FileStat implements vscode.FileStat {
|
||||
constructor(private fsStat: fs.Stats) {}
|
||||
|
||||
constructor(private fsStat: fs.Stats) { }
|
||||
|
||||
get type(): vscode.FileType {
|
||||
return this.fsStat.isFile()
|
||||
? vscode.FileType.File
|
||||
: this.fsStat.isDirectory()
|
||||
? vscode.FileType.Directory
|
||||
: this.fsStat.isSymbolicLink()
|
||||
? vscode.FileType.SymbolicLink
|
||||
: vscode.FileType.Unknown;
|
||||
return this.fsStat.isFile() ? vscode.FileType.File : this.fsStat.isDirectory() ? vscode.FileType.Directory : this.fsStat.isSymbolicLink() ? vscode.FileType.SymbolicLink : vscode.FileType.Unknown;
|
||||
}
|
||||
|
||||
get isFile(): boolean | undefined {
|
||||
@ -154,13 +145,14 @@ export class FileStat implements vscode.FileStat {
|
||||
}
|
||||
|
||||
interface Entry {
|
||||
uri: vscode.Uri;
|
||||
type: vscode.FileType;
|
||||
uri: vscode.Uri,
|
||||
type: vscode.FileType
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscode.FileSystemProvider {
|
||||
|
||||
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
|
||||
|
||||
constructor() {
|
||||
@ -171,28 +163,17 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
return this._onDidChangeFile.event;
|
||||
}
|
||||
|
||||
watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[] }): vscode.Disposable {
|
||||
const watcher = fs.watch(
|
||||
uri.fsPath,
|
||||
{ recursive: options.recursive },
|
||||
async (event: string, filename: string | Buffer) => {
|
||||
const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString()));
|
||||
watch(uri: vscode.Uri, options: { recursive: boolean; excludes: string[]; }): vscode.Disposable {
|
||||
const watcher = fs.watch(uri.fsPath, { recursive: options.recursive }, async (event: string, filename: string | Buffer) => {
|
||||
const filepath = path.join(uri.fsPath, _.normalizeNFC(filename.toString()));
|
||||
|
||||
// TODO support excludes (using minimatch library?)
|
||||
// TODO support excludes (using minimatch library?)
|
||||
|
||||
this._onDidChangeFile.fire([
|
||||
{
|
||||
type:
|
||||
event === 'change'
|
||||
? vscode.FileChangeType.Changed
|
||||
: (await _.exists(filepath))
|
||||
? vscode.FileChangeType.Created
|
||||
: vscode.FileChangeType.Deleted,
|
||||
uri: uri.with({ path: filepath })
|
||||
} as vscode.FileChangeEvent
|
||||
]);
|
||||
}
|
||||
);
|
||||
this._onDidChangeFile.fire([{
|
||||
type: event === 'change' ? vscode.FileChangeType.Changed : await _.exists(filepath) ? vscode.FileChangeType.Created : vscode.FileChangeType.Deleted,
|
||||
uri: uri.with({ path: filepath })
|
||||
} as vscode.FileChangeEvent]);
|
||||
});
|
||||
|
||||
return { dispose: () => watcher.close() };
|
||||
}
|
||||
@ -230,19 +211,11 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
return _.readfile(uri.fsPath);
|
||||
}
|
||||
|
||||
writeFile(
|
||||
uri: vscode.Uri,
|
||||
content: Uint8Array,
|
||||
options: { create: boolean; overwrite: boolean }
|
||||
): void | Thenable<void> {
|
||||
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): void | Thenable<void> {
|
||||
return this._writeFile(uri, content, options);
|
||||
}
|
||||
|
||||
async _writeFile(
|
||||
uri: vscode.Uri,
|
||||
content: Uint8Array,
|
||||
options: { create: boolean; overwrite: boolean }
|
||||
): Promise<void> {
|
||||
async _writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; }): Promise<void> {
|
||||
const exists = await _.exists(uri.fsPath);
|
||||
if (!exists) {
|
||||
if (!options.create) {
|
||||
@ -259,7 +232,7 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
return _.writefile(uri.fsPath, content as Buffer);
|
||||
}
|
||||
|
||||
delete(uri: vscode.Uri, options: { recursive: boolean }): void | Thenable<void> {
|
||||
delete(uri: vscode.Uri, options: { recursive: boolean; }): void | Thenable<void> {
|
||||
if (options.recursive) {
|
||||
return _.rmrf(uri.fsPath);
|
||||
}
|
||||
@ -267,11 +240,11 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
return _.unlink(uri.fsPath);
|
||||
}
|
||||
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): void | Thenable<void> {
|
||||
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): void | Thenable<void> {
|
||||
return this._rename(oldUri, newUri, options);
|
||||
}
|
||||
|
||||
async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): Promise<void> {
|
||||
async _rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; }): Promise<void> {
|
||||
const exists = await _.exists(newUri.fsPath);
|
||||
if (exists) {
|
||||
if (!options.overwrite) {
|
||||
@ -305,25 +278,17 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
return a[0].localeCompare(b[0]);
|
||||
}
|
||||
return a[1] === vscode.FileType.Directory ? -1 : 1;
|
||||
});
|
||||
return children.map(([name, type]) => ({
|
||||
uri: vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, name)),
|
||||
type
|
||||
}));
|
||||
})
|
||||
return children.map(([name, type]) => ({ uri: vscode.Uri.file(path.join(workspaceFolder.uri.fsPath, name)), type }));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getTreeItem(element: Entry): vscode.TreeItem {
|
||||
const treeItem = new vscode.TreeItem(
|
||||
element.uri,
|
||||
element.type === vscode.FileType.Directory
|
||||
? vscode.TreeItemCollapsibleState.Collapsed
|
||||
: vscode.TreeItemCollapsibleState.None
|
||||
);
|
||||
const treeItem = new vscode.TreeItem(element.uri, element.type === vscode.FileType.Directory ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None);
|
||||
if (element.type === vscode.FileType.File) {
|
||||
treeItem.command = { command: 'fileExplorer.openFile', title: 'Open File', arguments: [element.uri] };
|
||||
treeItem.command = { command: 'fileExplorer.openFile', title: "Open File", arguments: [element.uri], };
|
||||
treeItem.contextValue = 'file';
|
||||
}
|
||||
return treeItem;
|
||||
@ -331,15 +296,16 @@ export class FileSystemProvider implements vscode.TreeDataProvider<Entry>, vscod
|
||||
}
|
||||
|
||||
export class FileExplorer {
|
||||
|
||||
private fileExplorer: vscode.TreeView<Entry>;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
const treeDataProvider = new FileSystemProvider();
|
||||
this.fileExplorer = vscode.window.createTreeView('fileExplorer', { treeDataProvider });
|
||||
vscode.commands.registerCommand('fileExplorer.openFile', resource => this.openResource(resource));
|
||||
vscode.commands.registerCommand('fileExplorer.openFile', (resource) => this.openResource(resource));
|
||||
}
|
||||
|
||||
private openResource(resource: vscode.Uri): void {
|
||||
vscode.window.showTextDocument(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,14 +8,18 @@ interface IEntry {
|
||||
}
|
||||
|
||||
export interface FtpNode {
|
||||
|
||||
resource: vscode.Uri;
|
||||
isDirectory: boolean;
|
||||
|
||||
}
|
||||
|
||||
export class FtpModel {
|
||||
|
||||
private nodes: Map<string, FtpNode> = new Map<string, FtpNode>();
|
||||
|
||||
constructor(readonly host: string, private user: string, private password: string) {}
|
||||
constructor(readonly host: string, private user: string, private password: string) {
|
||||
}
|
||||
|
||||
public connect(): Thenable<Client> {
|
||||
return new Promise((c, e) => {
|
||||
@ -26,7 +30,7 @@ export class FtpModel {
|
||||
|
||||
client.on('error', error => {
|
||||
e('Error while connecting: ' + error.message);
|
||||
});
|
||||
})
|
||||
|
||||
client.connect({
|
||||
host: this.host,
|
||||
@ -46,14 +50,7 @@ export class FtpModel {
|
||||
|
||||
client.end();
|
||||
|
||||
return c(
|
||||
this.sort(
|
||||
list.map(entry => ({
|
||||
resource: vscode.Uri.parse(`ftp://${this.host}///${entry.name}`),
|
||||
isDirectory: entry.type === 'd'
|
||||
}))
|
||||
)
|
||||
);
|
||||
return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`ftp://${this.host}///${entry.name}`), isDirectory: entry.type === 'd' }))));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -69,14 +66,7 @@ export class FtpModel {
|
||||
|
||||
client.end();
|
||||
|
||||
return c(
|
||||
this.sort(
|
||||
list.map(entry => ({
|
||||
resource: vscode.Uri.parse(`${node.resource.fsPath}/${entry.name}`),
|
||||
isDirectory: entry.type === 'd'
|
||||
}))
|
||||
)
|
||||
);
|
||||
return c(this.sort(list.map(entry => ({ resource: vscode.Uri.parse(`${node.resource.fsPath}/${entry.name}`), isDirectory: entry.type === 'd' }))));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -104,15 +94,15 @@ export class FtpModel {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
let string = '';
|
||||
stream.on('data', function(buffer) {
|
||||
let string = ''
|
||||
stream.on('data', function (buffer) {
|
||||
if (buffer) {
|
||||
var part = buffer.toString();
|
||||
string += part;
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
stream.on('end', function () {
|
||||
client.end();
|
||||
c(string);
|
||||
});
|
||||
@ -123,26 +113,26 @@ export class FtpModel {
|
||||
}
|
||||
|
||||
export class FtpTreeDataProvider implements vscode.TreeDataProvider<FtpNode>, vscode.TextDocumentContentProvider {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
|
||||
readonly onDidChangeTreeData: vscode.Event<any> = this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(private readonly model: FtpModel) {}
|
||||
constructor(private readonly model: FtpModel) { }
|
||||
|
||||
public refresh(): any {
|
||||
this._onDidChangeTreeData.fire();
|
||||
}
|
||||
|
||||
|
||||
public getTreeItem(element: FtpNode): vscode.TreeItem {
|
||||
return {
|
||||
resourceUri: element.resource,
|
||||
collapsibleState: element.isDirectory ? vscode.TreeItemCollapsibleState.Collapsed : void 0,
|
||||
command: element.isDirectory
|
||||
? void 0
|
||||
: {
|
||||
command: 'ftpExplorer.openFtpResource',
|
||||
arguments: [element.resource],
|
||||
title: 'Open FTP Resource'
|
||||
}
|
||||
command: element.isDirectory ? void 0 : {
|
||||
command: 'ftpExplorer.openFtpResource',
|
||||
arguments: [element.resource],
|
||||
title: 'Open FTP Resource'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -161,6 +151,7 @@ export class FtpTreeDataProvider implements vscode.TreeDataProvider<FtpNode>, vs
|
||||
}
|
||||
|
||||
export class FtpExplorer {
|
||||
|
||||
private ftpViewer: vscode.TreeView<FtpNode>;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
@ -195,4 +186,4 @@ export class FtpExplorer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
tree-view-sample/src/jsftp.d.ts
vendored
57
tree-view-sample/src/jsftp.d.ts
vendored
@ -1,41 +1,46 @@
|
||||
|
||||
|
||||
import { Readable } from 'stream';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
declare namespace JSFtp {
|
||||
interface JSFtpOptions {
|
||||
host: string;
|
||||
port?: number | 21;
|
||||
user?: string | 'anonymous';
|
||||
pass?: string | '@anonymous';
|
||||
useList?: boolean;
|
||||
}
|
||||
|
||||
interface Callback<T> {
|
||||
(err: any, result: T): void;
|
||||
}
|
||||
|
||||
interface Entry {
|
||||
name: string;
|
||||
size: number;
|
||||
time: number;
|
||||
type: 0 | 1;
|
||||
}
|
||||
interface JSFtpOptions {
|
||||
host: string;
|
||||
port?: number | 21;
|
||||
user?: string | 'anonymous';
|
||||
pass?: string | '@anonymous';
|
||||
useList?: boolean
|
||||
}
|
||||
|
||||
interface Callback<T> {
|
||||
(err: any, result: T): void;
|
||||
}
|
||||
|
||||
|
||||
interface Entry {
|
||||
name: string;
|
||||
size: number;
|
||||
time: number;
|
||||
type: 0 | 1;
|
||||
}
|
||||
}
|
||||
|
||||
interface JSFtp extends EventEmitter {
|
||||
auth(user: string, password: string, callback: JSFtp.Callback<void>): void;
|
||||
keepAlive(wait?: number): void;
|
||||
ls(path: string, callback: JSFtp.Callback<JSFtp.Entry[]>): void;
|
||||
list(path: string, callback: JSFtp.Callback<any>): void;
|
||||
put(buffer: Buffer, path: string, callback: JSFtp.Callback<void>): void;
|
||||
get(path: string, callback: JSFtp.Callback<Readable>): void;
|
||||
setType(type: 'A' | 'AN' | 'AT' | 'AC' | 'E' | 'I' | 'L', callback: JSFtp.Callback<any>): void;
|
||||
raw(command: string, args: any[], callback: JSFtp.Callback<void>): void;
|
||||
raw<T>(command: string, args: any[], callback: JSFtp.Callback<T>): void;
|
||||
auth(user: string, password: string, callback: JSFtp.Callback<void>): void
|
||||
keepAlive(wait?: number): void;
|
||||
ls(path: string, callback: JSFtp.Callback<JSFtp.Entry[]>): void;
|
||||
list(path: string, callback: JSFtp.Callback<any>): void;
|
||||
put(buffer: Buffer, path: string, callback: JSFtp.Callback<void>): void;
|
||||
get(path: string, callback: JSFtp.Callback<Readable>): void;
|
||||
setType(type: 'A' | 'AN' | 'AT' | 'AC' | 'E' | 'I' | 'L', callback: JSFtp.Callback<any>): void;
|
||||
raw(command: string, args: any[], callback: JSFtp.Callback<void>): void;
|
||||
raw<T>(command: string, args: any[], callback: JSFtp.Callback<T>): void;
|
||||
}
|
||||
|
||||
interface JSFtpConstructor {
|
||||
new (options: JSFtp.JSFtpOptions): JSFtp;
|
||||
new(options: JSFtp.JSFtpOptions): JSFtp;
|
||||
}
|
||||
|
||||
declare const JSFtp: JSFtpConstructor;
|
||||
|
||||
@ -4,6 +4,7 @@ import * as path from 'path';
|
||||
import { isNumber } from 'util';
|
||||
|
||||
export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<number | null> = new vscode.EventEmitter<number | null>();
|
||||
readonly onDidChangeTreeData: vscode.Event<number | null> = this._onDidChangeTreeData.event;
|
||||
|
||||
@ -33,34 +34,30 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
}
|
||||
|
||||
rename(offset: number): void {
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter the new label' }).then(value => {
|
||||
if (value !== null && value !== undefined) {
|
||||
this.editor.edit(editBuilder => {
|
||||
const path = json.getLocation(this.text, offset).path;
|
||||
let propertyNode = json.findNodeAtLocation(this.tree, path);
|
||||
if (propertyNode.parent.type !== 'array') {
|
||||
propertyNode = propertyNode.parent.children[0];
|
||||
}
|
||||
const range = new vscode.Range(
|
||||
this.editor.document.positionAt(propertyNode.offset),
|
||||
this.editor.document.positionAt(propertyNode.offset + propertyNode.length)
|
||||
);
|
||||
editBuilder.replace(range, `"${value}"`);
|
||||
setTimeout(() => {
|
||||
this.parseTree();
|
||||
this.refresh(offset);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
vscode.window.showInputBox({ placeHolder: 'Enter the new label' })
|
||||
.then(value => {
|
||||
if (value !== null && value !== undefined) {
|
||||
this.editor.edit(editBuilder => {
|
||||
const path = json.getLocation(this.text, offset).path
|
||||
let propertyNode = json.findNodeAtLocation(this.tree, path);
|
||||
if (propertyNode.parent.type !== 'array') {
|
||||
propertyNode = propertyNode.parent.children[0];
|
||||
}
|
||||
const range = new vscode.Range(this.editor.document.positionAt(propertyNode.offset), this.editor.document.positionAt(propertyNode.offset + propertyNode.length));
|
||||
editBuilder.replace(range, `"${value}"`);
|
||||
setTimeout(() => {
|
||||
this.parseTree();
|
||||
this.refresh(offset);
|
||||
}, 100)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onActiveEditorChanged(): void {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
if (vscode.window.activeTextEditor.document.uri.scheme === 'file') {
|
||||
const enabled =
|
||||
vscode.window.activeTextEditor.document.languageId === 'json' ||
|
||||
vscode.window.activeTextEditor.document.languageId === 'jsonc';
|
||||
const enabled = vscode.window.activeTextEditor.document.languageId === 'json' || vscode.window.activeTextEditor.document.languageId === 'jsonc';
|
||||
vscode.commands.executeCommand('setContext', 'jsonOutlineEnabled', enabled);
|
||||
if (enabled) {
|
||||
this.refresh();
|
||||
@ -95,7 +92,7 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
|
||||
getChildren(offset?: number): Thenable<number[]> {
|
||||
if (offset) {
|
||||
const path = json.getLocation(this.text, offset).path;
|
||||
const path = json.getLocation(this.text, offset).path
|
||||
const node = json.findNodeAtLocation(this.tree, path);
|
||||
return Promise.resolve(this.getChildrenOffsets(node));
|
||||
} else {
|
||||
@ -106,7 +103,7 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
private getChildrenOffsets(node: json.Node): number[] {
|
||||
const offsets: number[] = [];
|
||||
for (const child of node.children) {
|
||||
const childPath = json.getLocation(this.text, child.offset).path;
|
||||
const childPath = json.getLocation(this.text, child.offset).path
|
||||
const childNode = json.findNodeAtLocation(this.tree, childPath);
|
||||
if (childNode) {
|
||||
offsets.push(childNode.offset);
|
||||
@ -116,27 +113,15 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
}
|
||||
|
||||
getTreeItem(offset: number): vscode.TreeItem {
|
||||
const path = json.getLocation(this.text, offset).path;
|
||||
const path = json.getLocation(this.text, offset).path
|
||||
const valueNode = json.findNodeAtLocation(this.tree, path);
|
||||
if (valueNode) {
|
||||
let hasChildren = valueNode.type === 'object' || valueNode.type === 'array';
|
||||
let treeItem: vscode.TreeItem = new vscode.TreeItem(
|
||||
this.getLabel(valueNode),
|
||||
hasChildren
|
||||
? valueNode.type === 'object'
|
||||
? vscode.TreeItemCollapsibleState.Expanded
|
||||
: vscode.TreeItemCollapsibleState.Collapsed
|
||||
: vscode.TreeItemCollapsibleState.None
|
||||
);
|
||||
let treeItem: vscode.TreeItem = new vscode.TreeItem(this.getLabel(valueNode), hasChildren ? valueNode.type === 'object' ? vscode.TreeItemCollapsibleState.Expanded : vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None);
|
||||
treeItem.command = {
|
||||
command: 'extension.openJsonSelection',
|
||||
title: '',
|
||||
arguments: [
|
||||
new vscode.Range(
|
||||
this.editor.document.positionAt(valueNode.offset),
|
||||
this.editor.document.positionAt(valueNode.offset + valueNode.length)
|
||||
)
|
||||
]
|
||||
arguments: [new vscode.Range(this.editor.document.positionAt(valueNode.offset), this.editor.document.positionAt(valueNode.offset + valueNode.length))]
|
||||
};
|
||||
treeItem.iconPath = this.getIcon(valueNode);
|
||||
treeItem.contextValue = valueNode.type;
|
||||
@ -155,19 +140,19 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
return {
|
||||
light: this.context.asAbsolutePath(path.join('resources', 'light', 'boolean.svg')),
|
||||
dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'boolean.svg'))
|
||||
};
|
||||
}
|
||||
}
|
||||
if (nodeType === 'string') {
|
||||
return {
|
||||
light: this.context.asAbsolutePath(path.join('resources', 'light', 'string.svg')),
|
||||
dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'string.svg'))
|
||||
};
|
||||
}
|
||||
}
|
||||
if (nodeType === 'number') {
|
||||
return {
|
||||
light: this.context.asAbsolutePath(path.join('resources', 'light', 'number.svg')),
|
||||
dark: this.context.asAbsolutePath(path.join('resources', 'dark', 'number.svg'))
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -182,7 +167,8 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
return prefix + ':[ ]';
|
||||
}
|
||||
return prefix + ':' + node.value.toString();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
const property = node.parent.children[0].value.toString();
|
||||
if (node.type === 'array' || node.type === 'object') {
|
||||
if (node.type === 'object') {
|
||||
@ -192,13 +178,8 @@ export class JsonOutlineProvider implements vscode.TreeDataProvider<number> {
|
||||
return '[ ] ' + property;
|
||||
}
|
||||
}
|
||||
const value = this.editor.document.getText(
|
||||
new vscode.Range(
|
||||
this.editor.document.positionAt(node.offset),
|
||||
this.editor.document.positionAt(node.offset + node.length)
|
||||
)
|
||||
);
|
||||
const value = this.editor.document.getText(new vscode.Range(this.editor.document.positionAt(node.offset), this.editor.document.positionAt(node.offset + node.length)))
|
||||
return `${property}: ${value}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,12 +3,12 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export class DepNodeProvider implements vscode.TreeDataProvider<Dependency> {
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<Dependency | undefined> = new vscode.EventEmitter<
|
||||
Dependency | undefined
|
||||
>();
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<Dependency | undefined> = new vscode.EventEmitter<Dependency | undefined>();
|
||||
readonly onDidChangeTreeData: vscode.Event<Dependency | undefined> = this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(private workspaceRoot: string) {}
|
||||
constructor(private workspaceRoot: string) {
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this._onDidChangeTreeData.fire();
|
||||
@ -23,11 +23,9 @@ export class DepNodeProvider implements vscode.TreeDataProvider<Dependency> {
|
||||
vscode.window.showInformationMessage('No dependency in empty workspace');
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
|
||||
if (element) {
|
||||
return Promise.resolve(
|
||||
this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json'))
|
||||
);
|
||||
return Promise.resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json')));
|
||||
} else {
|
||||
const packageJsonPath = path.join(this.workspaceRoot, 'package.json');
|
||||
if (this.pathExists(packageJsonPath)) {
|
||||
@ -37,6 +35,7 @@ export class DepNodeProvider implements vscode.TreeDataProvider<Dependency> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,7 +55,7 @@ export class DepNodeProvider implements vscode.TreeDataProvider<Dependency> {
|
||||
arguments: [moduleName]
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const deps = packageJson.dependencies
|
||||
? Object.keys(packageJson.dependencies).map(dep => toDep(dep, packageJson.dependencies[dep]))
|
||||
@ -82,6 +81,7 @@ export class DepNodeProvider implements vscode.TreeDataProvider<Dependency> {
|
||||
}
|
||||
|
||||
class Dependency extends vscode.TreeItem {
|
||||
|
||||
constructor(
|
||||
public readonly label: string,
|
||||
private version: string,
|
||||
@ -92,7 +92,7 @@ class Dependency extends vscode.TreeItem {
|
||||
}
|
||||
|
||||
get tooltip(): string {
|
||||
return `${this.label}-${this.version}`;
|
||||
return `${this.label}-${this.version}`
|
||||
}
|
||||
|
||||
iconPath = {
|
||||
@ -101,4 +101,5 @@ class Dependency extends vscode.TreeItem {
|
||||
};
|
||||
|
||||
contextValue = 'dependency';
|
||||
|
||||
}
|
||||
|
||||
@ -14,9 +14,9 @@ export enum Mode {
|
||||
}
|
||||
|
||||
export interface ModifierKeys {
|
||||
ctrl?: boolean;
|
||||
alt?: boolean;
|
||||
shifit?: boolean;
|
||||
ctrl?: boolean,
|
||||
alt?: boolean,
|
||||
shifit?: boolean
|
||||
}
|
||||
|
||||
export class DeleteRegister {
|
||||
@ -42,10 +42,12 @@ export interface IController {
|
||||
}
|
||||
|
||||
export abstract class AbstractCommandDescriptor {
|
||||
|
||||
public abstract createCommand(args?: any): Command;
|
||||
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
commandId: string;
|
||||
args?: any[];
|
||||
commandId: string,
|
||||
args?: any[]
|
||||
}
|
||||
|
||||
@ -4,7 +4,15 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TextEditorCursorStyle, Position, Range, Selection, TextEditor, TextEditorRevealType, window } from 'vscode';
|
||||
import {
|
||||
TextEditorCursorStyle,
|
||||
Position,
|
||||
Range,
|
||||
Selection,
|
||||
TextEditor,
|
||||
TextEditorRevealType,
|
||||
window
|
||||
} from 'vscode';
|
||||
|
||||
import { Words } from './words';
|
||||
import { MotionState, Motion } from './motions';
|
||||
@ -17,28 +25,19 @@ export interface ITypeResult {
|
||||
}
|
||||
|
||||
export class Controller implements IController {
|
||||
|
||||
private _currentMode: Mode;
|
||||
private _currentInput: string;
|
||||
private _motionState: MotionState;
|
||||
private _isVisual: boolean;
|
||||
|
||||
public get motionState(): MotionState {
|
||||
return this._motionState;
|
||||
}
|
||||
public findMotion(input: string): Motion {
|
||||
return Mappings.findMotion(input);
|
||||
}
|
||||
public isMotionPrefix(input: string): boolean {
|
||||
return Mappings.isMotionPrefix(input);
|
||||
}
|
||||
public get motionState(): MotionState { return this._motionState; }
|
||||
public findMotion(input: string): Motion { return Mappings.findMotion(input); }
|
||||
public isMotionPrefix(input: string): boolean { return Mappings.isMotionPrefix(input); }
|
||||
|
||||
private _deleteRegister: DeleteRegister;
|
||||
public setDeleteRegister(register: DeleteRegister): void {
|
||||
this._deleteRegister = register;
|
||||
}
|
||||
public getDeleteRegister(): DeleteRegister {
|
||||
return this._deleteRegister;
|
||||
}
|
||||
public setDeleteRegister(register: DeleteRegister): void { this._deleteRegister = register; }
|
||||
public getDeleteRegister(): DeleteRegister { return this._deleteRegister; }
|
||||
|
||||
constructor() {
|
||||
this._motionState = new MotionState();
|
||||
@ -181,13 +180,11 @@ export class Controller implements IController {
|
||||
|
||||
if (this._currentMode === Mode.REPLACE) {
|
||||
let pos = editor.selection.active;
|
||||
editor
|
||||
.edit(builder => {
|
||||
builder.replace(new Range(pos.line, pos.character, pos.line, pos.character + 1), text);
|
||||
})
|
||||
.then(() => {
|
||||
setPositionAndReveal(editor, pos.line, pos.character + 1);
|
||||
});
|
||||
editor.edit((builder) => {
|
||||
builder.replace(new Range(pos.line, pos.character, pos.line, pos.character + 1), text);
|
||||
}).then(() => {
|
||||
setPositionAndReveal(editor, pos.line, pos.character + 1);
|
||||
});
|
||||
|
||||
return Promise.resolve({
|
||||
hasConsumedInput: true,
|
||||
@ -210,7 +207,7 @@ export class Controller implements IController {
|
||||
|
||||
if (this._currentMode === Mode.REPLACE) {
|
||||
let pos = editor.selection.active;
|
||||
editor.edit(builder => {
|
||||
editor.edit((builder) => {
|
||||
builder.replace(new Range(pos.line, pos.character - replaceCharCnt, pos.line, pos.character), text);
|
||||
});
|
||||
|
||||
@ -222,7 +219,7 @@ export class Controller implements IController {
|
||||
|
||||
private _interpretNormalModeInput(editor: TextEditor, modifierKeys: ModifierKeys): Thenable<ITypeResult> {
|
||||
if (this._currentInput.startsWith(':')) {
|
||||
return window.showInputBox({ value: 'tabm' }).then(value => {
|
||||
return window.showInputBox({ value: 'tabm' }).then((value) => {
|
||||
return this._findMapping(value || '', editor, modifierKeys);
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(vscode.commands.registerCommand(commandId, run));
|
||||
}
|
||||
function registerCtrlKeyBinding(key: string): void {
|
||||
registerCommandNice(key, function(args) {
|
||||
registerCommandNice(key, function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
@ -28,34 +28,34 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
let vimExt = new VimExt();
|
||||
|
||||
registerCommandNice('type', function(args) {
|
||||
registerCommandNice('type', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.type(args.text);
|
||||
});
|
||||
registerCommandNice('replacePreviousChar', function(args) {
|
||||
registerCommandNice('replacePreviousChar', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.replacePrevChar(args.text, args.replaceCharCnt);
|
||||
});
|
||||
registerCommandNice('compositionStart', function(args) {
|
||||
registerCommandNice('compositionStart', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.compositionStart();
|
||||
});
|
||||
registerCommandNice('compositionEnd', function(args) {
|
||||
registerCommandNice('compositionEnd', function (args) {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
vimExt.compositionEnd();
|
||||
});
|
||||
registerCommandNice('vim.goToNormalMode', function(args) {
|
||||
registerCommandNice('vim.goToNormalMode', function (args) {
|
||||
vimExt.goToNormalMode();
|
||||
});
|
||||
registerCommandNice('vim.clearInput', function(args) {
|
||||
registerCommandNice('vim.clearInput', function (args) {
|
||||
vimExt.clearInput();
|
||||
});
|
||||
// registerCommandNice('paste', function(args) {
|
||||
@ -84,6 +84,7 @@ function getConfiguredWordSeparators(): string {
|
||||
}
|
||||
|
||||
class VimExt {
|
||||
|
||||
private _inNormalMode: ContextKey;
|
||||
private _hasInput: ContextKey;
|
||||
private _statusBar: StatusBar;
|
||||
@ -94,16 +95,16 @@ class VimExt {
|
||||
this._hasInput = new ContextKey('vim.hasInput');
|
||||
this._statusBar = new StatusBar();
|
||||
|
||||
this._controller = new Controller();
|
||||
this._controller = new Controller()
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(textEditor => {
|
||||
vscode.window.onDidChangeActiveTextEditor((textEditor) => {
|
||||
if (!textEditor) {
|
||||
return;
|
||||
}
|
||||
this._ensureState();
|
||||
});
|
||||
|
||||
vscode.window.onDidChangeTextEditorSelection(e => {
|
||||
vscode.window.onDidChangeTextEditorSelection((e) => {
|
||||
let isVisual = this._controller.getVisual();
|
||||
|
||||
if (!isVisual) {
|
||||
@ -153,7 +154,7 @@ class VimExt {
|
||||
}
|
||||
|
||||
public type(text: string, modifierKeys: ModifierKeys = { ctrl: false, shifit: false, alt: false }): void {
|
||||
this._controller.type(vscode.window.activeTextEditor, text, modifierKeys).then(r => {
|
||||
this._controller.type(vscode.window.activeTextEditor, text, modifierKeys).then((r) => {
|
||||
if (r.hasConsumedInput) {
|
||||
this._ensureState();
|
||||
if (r.executeEditorCommand) {
|
||||
@ -185,7 +186,7 @@ class VimExt {
|
||||
}
|
||||
|
||||
public compositionEnd(): void {
|
||||
this._controller.compositionEnd(vscode.window.activeTextEditor).then(r => {
|
||||
this._controller.compositionEnd(vscode.window.activeTextEditor).then((r) => {
|
||||
if (r.hasConsumedInput) {
|
||||
this._ensureState();
|
||||
if (r.executeEditorCommand) {
|
||||
|
||||
@ -9,47 +9,43 @@ import { Motion, Motions } from './motions';
|
||||
import { Operator, Operators } from './operators';
|
||||
import { IController, Command, AbstractCommandDescriptor, ModifierKeys } from './common';
|
||||
|
||||
const CHAR_TO_BINDING: { [char: string]: any } = {};
|
||||
const CHAR_TO_BINDING: { [char: string]: any; } = {};
|
||||
function defineBinding(char: string, value: any, modifierKeys: ModifierKeys): void {
|
||||
let key = modifierKeys.ctrl ? 'CTRL + ' + char : char;
|
||||
CHAR_TO_BINDING[key] = value;
|
||||
}
|
||||
};
|
||||
function getBinding(char: string, modifierKeys: ModifierKeys): any {
|
||||
let key = modifierKeys.ctrl ? 'CTRL + ' + char : char;
|
||||
return CHAR_TO_BINDING[key];
|
||||
}
|
||||
};
|
||||
|
||||
function defineOperator(char: string, operator: Operator, modifierKeys: ModifierKeys = {}): void {
|
||||
defineBinding(char + '__operator__', operator, modifierKeys);
|
||||
}
|
||||
};
|
||||
function getOperator(char: string, modifierKeys: ModifierKeys = {}): Operator {
|
||||
return getBinding(char + '__operator__', modifierKeys);
|
||||
}
|
||||
};
|
||||
|
||||
function defineCommand(char: string, commandId: string, modifierKeys: ModifierKeys = {}): void {
|
||||
defineBinding(char + '__command__', { commandId: commandId }, modifierKeys);
|
||||
}
|
||||
};
|
||||
function getCommand(char: string, modifierKeys: ModifierKeys = {}): Command {
|
||||
return getBinding(char + '__command__', modifierKeys);
|
||||
}
|
||||
};
|
||||
|
||||
function defineMotion(char: string, motion: Motion, modifierKeys: ModifierKeys = {}): void {
|
||||
defineBinding(char + '__motion__', motion, modifierKeys);
|
||||
}
|
||||
};
|
||||
function getMotion(char: string, modifierKeys: ModifierKeys = {}): Motion {
|
||||
return getBinding(char + '__motion__', modifierKeys);
|
||||
}
|
||||
};
|
||||
|
||||
function defineMotionCommand(
|
||||
char: string,
|
||||
motionCommand: AbstractCommandDescriptor,
|
||||
modifierKeys: ModifierKeys = {}
|
||||
): void {
|
||||
function defineMotionCommand(char: string, motionCommand: AbstractCommandDescriptor, modifierKeys: ModifierKeys = {}): void {
|
||||
defineBinding(char + '__motioncommand__', motionCommand, modifierKeys);
|
||||
}
|
||||
};
|
||||
function getMotionCommand(char: string, modifierKeys: ModifierKeys = {}): AbstractCommandDescriptor {
|
||||
return getBinding(char + '__motioncommand__', modifierKeys);
|
||||
}
|
||||
};
|
||||
|
||||
// Operators
|
||||
defineOperator('x', Operators.DeleteCharUnderCursor);
|
||||
@ -124,12 +120,14 @@ defineMotionCommand('zb', Motions.RevealCurrentLineAtBottom);
|
||||
defineMotionCommand('zc', Motions.FoldUnder);
|
||||
defineMotionCommand('zo', Motions.UnfoldUnder);
|
||||
|
||||
|
||||
export interface IFoundOperator {
|
||||
runNormal(controller: IController, editor: TextEditor): boolean;
|
||||
runVisual(controller: IController, editor: TextEditor): boolean;
|
||||
}
|
||||
|
||||
export class Mappings {
|
||||
|
||||
public static findMotion(input: string): Motion {
|
||||
let parsed = _parseNumberAndString(input);
|
||||
let motion = getMotion(parsed.input.substr(0, 1));
|
||||
@ -152,11 +150,7 @@ export class Mappings {
|
||||
return command;
|
||||
}
|
||||
|
||||
private static findMotionCommandFromNumberAndString(
|
||||
numberAndString: INumberAndString,
|
||||
isVisual: boolean,
|
||||
modifierKeys: ModifierKeys
|
||||
): Command {
|
||||
private static findMotionCommandFromNumberAndString(numberAndString: INumberAndString, isVisual: boolean, modifierKeys: ModifierKeys): Command {
|
||||
let motionCommand = getMotionCommand(numberAndString.input.substr(0, 1), modifierKeys);
|
||||
if (!motionCommand) {
|
||||
motionCommand = getMotionCommand(numberAndString.input.substr(0, 2), modifierKeys);
|
||||
@ -170,12 +164,7 @@ export class Mappings {
|
||||
if (!motionCommand) {
|
||||
motionCommand = getMotionCommand(numberAndString.input, modifierKeys);
|
||||
}
|
||||
return motionCommand
|
||||
? motionCommand.createCommand({
|
||||
isVisual: isVisual,
|
||||
repeat: numberAndString.hasRepeatCount ? numberAndString.repeatCount : undefined
|
||||
})
|
||||
: null;
|
||||
return motionCommand ? motionCommand.createCommand({ isVisual: isVisual, repeat: numberAndString.hasRepeatCount ? numberAndString.repeatCount : undefined }) : null;
|
||||
}
|
||||
|
||||
public static findOperator(input: string, modifierKeys: ModifierKeys): IFoundOperator {
|
||||
@ -234,7 +223,7 @@ function _parseNumberAndString(input: string, numberAtBeginning: boolean = true)
|
||||
hasRepeatCount: false,
|
||||
repeatCount: 1,
|
||||
input: input
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface INumberAndString {
|
||||
|
||||
@ -9,6 +9,7 @@ import { Words, WordCharacters } from './words';
|
||||
import { Command, AbstractCommandDescriptor } from './common';
|
||||
|
||||
export class MotionState {
|
||||
|
||||
public anchor: Position;
|
||||
public cursorDesiredCharacter: number;
|
||||
public wordCharacterClass: WordCharacters;
|
||||
@ -18,6 +19,7 @@ export class MotionState {
|
||||
this.wordCharacterClass = null;
|
||||
this.anchor = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export abstract class Motion {
|
||||
@ -32,6 +34,7 @@ export abstract class Motion {
|
||||
}
|
||||
|
||||
class RepeatingMotion extends Motion {
|
||||
|
||||
private _actual: Motion;
|
||||
private _repeatCount: number;
|
||||
|
||||
@ -53,7 +56,7 @@ class NextCharacterMotion extends Motion {
|
||||
public run(doc: TextDocument, pos: Position, state: MotionState): Position {
|
||||
if (pos.character === doc.lineAt(pos.line).text.length) {
|
||||
// on last character
|
||||
return pos.line + 1 < doc.lineCount ? new Position(pos.line + 1, 0) : pos;
|
||||
return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos);
|
||||
}
|
||||
|
||||
return new Position(pos.line, pos.character + 1);
|
||||
@ -77,7 +80,7 @@ class DownMotion extends Motion {
|
||||
public run(doc: TextDocument, pos: Position, state: MotionState): Position {
|
||||
let line = pos.line;
|
||||
|
||||
state.cursorDesiredCharacter = state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter;
|
||||
state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter);
|
||||
|
||||
if (line < doc.lineCount - 1) {
|
||||
line++;
|
||||
@ -92,7 +95,7 @@ class UpMotion extends Motion {
|
||||
public run(doc: TextDocument, pos: Position, state: MotionState): Position {
|
||||
let line = pos.line;
|
||||
|
||||
state.cursorDesiredCharacter = state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter;
|
||||
state.cursorDesiredCharacter = (state.cursorDesiredCharacter === -1 ? pos.character : state.cursorDesiredCharacter);
|
||||
|
||||
if (line > 0) {
|
||||
line--;
|
||||
@ -135,7 +138,7 @@ class NextWordStartMotion extends Motion {
|
||||
|
||||
if (pos.character >= lineContent.length - 1) {
|
||||
// cursor at end of line
|
||||
return pos.line + 1 < doc.lineCount ? new Position(pos.line + 1, 0) : pos;
|
||||
return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos);
|
||||
}
|
||||
|
||||
let nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass);
|
||||
@ -168,7 +171,7 @@ class NextWordEndMotion extends Motion {
|
||||
|
||||
if (pos.character >= lineContent.length - 1) {
|
||||
// no content on this line or cursor at end of line
|
||||
return pos.line + 1 < doc.lineCount ? new Position(pos.line + 1, 0) : pos;
|
||||
return ((pos.line + 1 < doc.lineCount) ? new Position(pos.line + 1, 0) : pos);
|
||||
}
|
||||
|
||||
let nextWord = Words.findNextWord(doc, pos, state.wordCharacterClass);
|
||||
@ -198,6 +201,7 @@ class GoToLineUndefinedMotion extends Motion {
|
||||
}
|
||||
|
||||
abstract class GoToLineMotion extends Motion {
|
||||
|
||||
protected firstNonWhitespaceChar(doc: TextDocument, line: number): number {
|
||||
let lineContent = doc.lineAt(line).text;
|
||||
let character = 0;
|
||||
@ -210,6 +214,7 @@ abstract class GoToLineMotion extends Motion {
|
||||
}
|
||||
return character;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GoToFirstLineMotion extends GoToLineMotion {
|
||||
@ -240,6 +245,7 @@ class GoToLineDefinedMotion extends GoToLineMotion {
|
||||
}
|
||||
|
||||
class CursorMoveCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor(private to: string, private by?: string) {
|
||||
super();
|
||||
}
|
||||
@ -250,7 +256,7 @@ class CursorMoveCommand extends AbstractCommandDescriptor {
|
||||
by: this.by,
|
||||
value: args.repeat || 1,
|
||||
select: !!args.isVisual
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'cursorMove',
|
||||
args: cursorMoveArgs
|
||||
@ -259,6 +265,7 @@ class CursorMoveCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
|
||||
class EditorScrollCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor(private to: string, private by?: string) {
|
||||
super();
|
||||
}
|
||||
@ -269,7 +276,7 @@ class EditorScrollCommand extends AbstractCommandDescriptor {
|
||||
by: this.by,
|
||||
value: args.repeat || 1,
|
||||
revealCursor: true
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'editorScroll',
|
||||
args: editorScrollArgs
|
||||
@ -278,6 +285,7 @@ class EditorScrollCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
|
||||
class RevealCurrentLineCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor(private at: string) {
|
||||
super();
|
||||
}
|
||||
@ -296,6 +304,7 @@ class RevealCurrentLineCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
|
||||
class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@ -304,7 +313,7 @@ class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor {
|
||||
let moveActiveEditorArgs: any = {
|
||||
to: args.repeat === void 0 ? 'last' : 'position',
|
||||
value: args.repeat !== void 0 ? args.repeat + 1 : undefined
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'moveActiveEditor',
|
||||
args: moveActiveEditorArgs
|
||||
@ -313,6 +322,7 @@ class MoveActiveEditorCommandByPosition extends AbstractCommandDescriptor {
|
||||
}
|
||||
|
||||
class MoveActiveEditorCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor(private to: string) {
|
||||
super();
|
||||
}
|
||||
@ -321,7 +331,7 @@ class MoveActiveEditorCommand extends AbstractCommandDescriptor {
|
||||
let moveActiveEditorArgs: any = {
|
||||
to: this.to,
|
||||
value: args.repeat ? args.repeat : 1
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'moveActiveEditor',
|
||||
args: moveActiveEditorArgs
|
||||
@ -329,6 +339,7 @@ class MoveActiveEditorCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
}
|
||||
class FoldCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@ -337,7 +348,7 @@ class FoldCommand extends AbstractCommandDescriptor {
|
||||
let foldEditorArgs: any = {
|
||||
levels: args.repeat ? args.repeat : 1,
|
||||
direction: 'up'
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'editor.fold',
|
||||
args: foldEditorArgs
|
||||
@ -346,6 +357,7 @@ class FoldCommand extends AbstractCommandDescriptor {
|
||||
}
|
||||
|
||||
class UnfoldCommand extends AbstractCommandDescriptor {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@ -354,7 +366,7 @@ class UnfoldCommand extends AbstractCommandDescriptor {
|
||||
let foldEditorArgs: any = {
|
||||
levels: args.repeat ? args.repeat : 1,
|
||||
direction: 'up'
|
||||
};
|
||||
}
|
||||
return {
|
||||
commandId: 'editor.unfold',
|
||||
args: foldEditorArgs
|
||||
|
||||
@ -9,6 +9,7 @@ import { MotionState, Motion, Motions } from './motions';
|
||||
import { Mode, IController, DeleteRegister } from './common';
|
||||
|
||||
export abstract class Operator {
|
||||
|
||||
public abstract runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean;
|
||||
public abstract runVisualMode(ctrl: IController, ed: TextEditor, args: string): boolean;
|
||||
|
||||
@ -31,7 +32,7 @@ export abstract class Operator {
|
||||
|
||||
protected delete(ctrl: IController, ed: TextEditor, isWholeLine: boolean, range: Range): void {
|
||||
ctrl.setDeleteRegister(new DeleteRegister(isWholeLine, ed.document.getText(range)));
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.delete(range);
|
||||
});
|
||||
}
|
||||
@ -80,11 +81,7 @@ class VisualOperator extends OperatorWithNoArgs {
|
||||
|
||||
class DeleteCharUnderCursorOperator extends Operator {
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
let to = Motions.NextCharacter.repeat(repeatCount > 1, repeatCount).run(
|
||||
this.doc(ed),
|
||||
this.pos(ed),
|
||||
ctrl.motionState
|
||||
);
|
||||
let to = Motions.NextCharacter.repeat(repeatCount > 1, repeatCount).run(this.doc(ed), this.pos(ed), ctrl.motionState);
|
||||
let from = this.pos(ed);
|
||||
|
||||
this.delete(ctrl, ed, false, new Range(from.line, from.character, to.line, to.character));
|
||||
@ -137,6 +134,7 @@ abstract class OperatorWithMotion extends Operator {
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
let motion = ctrl.findMotion(args);
|
||||
if (!motion) {
|
||||
|
||||
// is it motion building
|
||||
if (ctrl.isMotionPrefix(args)) {
|
||||
return false;
|
||||
@ -153,6 +151,7 @@ abstract class OperatorWithMotion extends Operator {
|
||||
}
|
||||
|
||||
class DeleteToOperator extends OperatorWithMotion {
|
||||
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
if (args === 'd') {
|
||||
// dd
|
||||
@ -178,6 +177,7 @@ class DeleteToOperator extends OperatorWithMotion {
|
||||
}
|
||||
|
||||
class PutOperator extends Operator {
|
||||
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
let register = ctrl.getDeleteRegister();
|
||||
if (!register) {
|
||||
@ -189,7 +189,7 @@ class PutOperator extends Operator {
|
||||
|
||||
let pos = this.pos(ed);
|
||||
if (!register.isWholeLine) {
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.insert(new Position(pos.line, pos.character + 1), str);
|
||||
});
|
||||
return true;
|
||||
@ -206,7 +206,7 @@ class PutOperator extends Operator {
|
||||
str = '\n' + str;
|
||||
}
|
||||
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.insert(new Position(insertLine, insertCharacter), str);
|
||||
});
|
||||
|
||||
@ -223,7 +223,7 @@ class PutOperator extends Operator {
|
||||
let str = register.content;
|
||||
|
||||
let sel = this.sel(ed);
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.replace(sel, str);
|
||||
});
|
||||
|
||||
@ -232,6 +232,7 @@ class PutOperator extends Operator {
|
||||
}
|
||||
|
||||
class ReplaceOperator extends Operator {
|
||||
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
if (args.length === 0) {
|
||||
// input not ready
|
||||
@ -246,7 +247,7 @@ class ReplaceOperator extends Operator {
|
||||
return true;
|
||||
}
|
||||
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.replace(new Range(pos.line, pos.character, pos.line, toCharacter), repeatString(args, repeatCount));
|
||||
});
|
||||
|
||||
@ -273,7 +274,7 @@ class ReplaceOperator extends Operator {
|
||||
}
|
||||
}
|
||||
|
||||
ed.edit(builder => {
|
||||
ed.edit((builder) => {
|
||||
builder.replace(sel, dstString);
|
||||
});
|
||||
|
||||
@ -282,6 +283,7 @@ class ReplaceOperator extends Operator {
|
||||
}
|
||||
|
||||
class ReplaceModeOperator extends Operator {
|
||||
|
||||
public runNormalMode(ctrl: IController, ed: TextEditor, repeatCount: number, args: string): boolean {
|
||||
ctrl.setMode(Mode.REPLACE);
|
||||
return true;
|
||||
@ -292,9 +294,11 @@ class ReplaceModeOperator extends Operator {
|
||||
ctrl.setMode(Mode.INSERT);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ChangeOperator extends OperatorWithMotion {
|
||||
|
||||
protected _runNormalMode(ctrl: IController, ed: TextEditor, motion: Motion): boolean {
|
||||
let to = motion.run(this.doc(ed), this.pos(ed), ctrl.motionState);
|
||||
let from = this.pos(ed);
|
||||
@ -336,5 +340,5 @@ export const Operators = {
|
||||
Put: new PutOperator(),
|
||||
Replace: new ReplaceOperator(),
|
||||
Change: new ChangeOperator(),
|
||||
ReplaceMode: new ReplaceModeOperator()
|
||||
ReplaceMode: new ReplaceModeOperator(),
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ export interface IWord {
|
||||
}
|
||||
|
||||
export class Words {
|
||||
|
||||
public static createWordCharacters(wordSeparators: string): WordCharacters {
|
||||
let result: CharacterClass[] = [];
|
||||
|
||||
@ -46,67 +47,43 @@ export class Words {
|
||||
}
|
||||
|
||||
public static findNextWord(doc: TextDocument, pos: Position, wordCharacterClass: WordCharacters): IWord {
|
||||
|
||||
let lineContent = doc.lineAt(pos.line).text;
|
||||
let wordType = WordType.NONE;
|
||||
let len = lineContent.length;
|
||||
|
||||
for (let chIndex = pos.character; chIndex < len; chIndex++) {
|
||||
let chCode = lineContent.charCodeAt(chIndex);
|
||||
let chClass = wordCharacterClass[chCode] || CharacterClass.REGULAR;
|
||||
let chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR);
|
||||
|
||||
if (chClass === CharacterClass.REGULAR) {
|
||||
if (wordType === WordType.SEPARATOR) {
|
||||
return this._createWord(
|
||||
lineContent,
|
||||
wordType,
|
||||
this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1),
|
||||
chIndex
|
||||
);
|
||||
return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex);
|
||||
}
|
||||
wordType = WordType.REGULAR;
|
||||
} else if (chClass === CharacterClass.WORD_SEPARATOR) {
|
||||
if (wordType === WordType.REGULAR) {
|
||||
return this._createWord(
|
||||
lineContent,
|
||||
wordType,
|
||||
this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1),
|
||||
chIndex
|
||||
);
|
||||
return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex);
|
||||
}
|
||||
wordType = WordType.SEPARATOR;
|
||||
} else if (chClass === CharacterClass.WHITESPACE) {
|
||||
if (wordType !== WordType.NONE) {
|
||||
return this._createWord(
|
||||
lineContent,
|
||||
wordType,
|
||||
this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1),
|
||||
chIndex
|
||||
);
|
||||
return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, chIndex - 1), chIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wordType !== WordType.NONE) {
|
||||
return this._createWord(
|
||||
lineContent,
|
||||
wordType,
|
||||
this._findStartOfWord(lineContent, wordCharacterClass, wordType, len - 1),
|
||||
len
|
||||
);
|
||||
return this._createWord(lineContent, wordType, this._findStartOfWord(lineContent, wordCharacterClass, wordType, len - 1), len);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findStartOfWord(
|
||||
lineContent: string,
|
||||
wordCharacterClass: WordCharacters,
|
||||
wordType: WordType,
|
||||
startIndex: number
|
||||
): number {
|
||||
private static _findStartOfWord(lineContent: string, wordCharacterClass: WordCharacters, wordType: WordType, startIndex: number): number {
|
||||
for (let chIndex = startIndex; chIndex >= 0; chIndex--) {
|
||||
let chCode = lineContent.charCodeAt(chIndex);
|
||||
let chClass = wordCharacterClass[chCode] || CharacterClass.REGULAR;
|
||||
let chClass = (wordCharacterClass[chCode] || CharacterClass.REGULAR);
|
||||
|
||||
if (chClass === CharacterClass.WHITESPACE) {
|
||||
return chIndex + 1;
|
||||
|
||||
@ -8,9 +8,10 @@ import * as vscode from 'vscode';
|
||||
import { add } from './math';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
let disposable = vscode.commands.registerCommand('extension.helloWebpack', () => {
|
||||
vscode.window.showInformationMessage(`41 + 1 = ${add(41, 1)}`);
|
||||
});
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
let disposable = vscode.commands.registerCommand('extension.helloWebpack', () => {
|
||||
vscode.window.showInformationMessage(`41 + 1 = ${add(41, 1)}`);
|
||||
});
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
'use strict';
|
||||
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export function sub(a: number, b: number): number {
|
||||
return a - b;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
@ -2,173 +2,164 @@ import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
const cats = {
|
||||
'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
|
||||
'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif',
|
||||
'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif'
|
||||
'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
|
||||
'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif',
|
||||
'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif'
|
||||
};
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('catCoding.start', () => {
|
||||
CatCodingPanel.createOrShow(context.extensionPath);
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('catCoding.doRefactor', () => {
|
||||
if (CatCodingPanel.currentPanel) {
|
||||
CatCodingPanel.currentPanel.doRefactor();
|
||||
}
|
||||
})
|
||||
);
|
||||
context.subscriptions.push(vscode.commands.registerCommand('catCoding.start', () => {
|
||||
CatCodingPanel.createOrShow(context.extensionPath);
|
||||
}));
|
||||
|
||||
if (vscode.window.registerWebviewPanelSerializer) {
|
||||
// Make sure we register a serilizer in activation event
|
||||
vscode.window.registerWebviewPanelSerializer(CatCodingPanel.viewType, {
|
||||
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
|
||||
console.log(`Got state: ${state}`);
|
||||
CatCodingPanel.revive(webviewPanel, context.extensionPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
context.subscriptions.push(vscode.commands.registerCommand('catCoding.doRefactor', () => {
|
||||
if (CatCodingPanel.currentPanel) {
|
||||
CatCodingPanel.currentPanel.doRefactor();
|
||||
}
|
||||
}));
|
||||
|
||||
if (vscode.window.registerWebviewPanelSerializer) {
|
||||
// Make sure we register a serilizer in activation event
|
||||
vscode.window.registerWebviewPanelSerializer(CatCodingPanel.viewType, {
|
||||
async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
|
||||
console.log(`Got state: ${state}`);
|
||||
CatCodingPanel.revive(webviewPanel, context.extensionPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages cat coding webview panels
|
||||
*/
|
||||
class CatCodingPanel {
|
||||
/**
|
||||
* Track the currently panel. Only allow a single panel to exist at a time.
|
||||
*/
|
||||
public static currentPanel: CatCodingPanel | undefined;
|
||||
/**
|
||||
* Track the currently panel. Only allow a single panel to exist at a time.
|
||||
*/
|
||||
public static currentPanel: CatCodingPanel | undefined;
|
||||
|
||||
public static readonly viewType = 'catCoding';
|
||||
public static readonly viewType = 'catCoding';
|
||||
|
||||
private readonly _panel: vscode.WebviewPanel;
|
||||
private readonly _extensionPath: string;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
private readonly _panel: vscode.WebviewPanel;
|
||||
private readonly _extensionPath: string;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
public static createOrShow(extensionPath: string) {
|
||||
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;
|
||||
public static createOrShow(extensionPath: string) {
|
||||
const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined;
|
||||
|
||||
// If we already have a panel, show it.
|
||||
if (CatCodingPanel.currentPanel) {
|
||||
CatCodingPanel.currentPanel._panel.reveal(column);
|
||||
return;
|
||||
}
|
||||
// If we already have a panel, show it.
|
||||
if (CatCodingPanel.currentPanel) {
|
||||
CatCodingPanel.currentPanel._panel.reveal(column);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, create a new panel.
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
CatCodingPanel.viewType,
|
||||
'Cat Coding',
|
||||
column || vscode.ViewColumn.One,
|
||||
{
|
||||
// Enable javascript in the webview
|
||||
enableScripts: true,
|
||||
// Otherwise, create a new panel.
|
||||
const panel = vscode.window.createWebviewPanel(CatCodingPanel.viewType, "Cat Coding", column || vscode.ViewColumn.One, {
|
||||
// Enable javascript in the webview
|
||||
enableScripts: true,
|
||||
|
||||
// And restric the webview to only loading content from our extension's `media` directory.
|
||||
localResourceRoots: [vscode.Uri.file(path.join(extensionPath, 'media'))]
|
||||
}
|
||||
);
|
||||
// And restric the webview to only loading content from our extension's `media` directory.
|
||||
localResourceRoots: [
|
||||
vscode.Uri.file(path.join(extensionPath, 'media'))
|
||||
]
|
||||
});
|
||||
|
||||
CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath);
|
||||
}
|
||||
CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath);
|
||||
}
|
||||
|
||||
public static revive(panel: vscode.WebviewPanel, extensionPath: string) {
|
||||
CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath);
|
||||
}
|
||||
public static revive(panel: vscode.WebviewPanel, extensionPath: string) {
|
||||
CatCodingPanel.currentPanel = new CatCodingPanel(panel, extensionPath);
|
||||
}
|
||||
|
||||
private constructor(panel: vscode.WebviewPanel, extensionPath: string) {
|
||||
this._panel = panel;
|
||||
this._extensionPath = extensionPath;
|
||||
private constructor(
|
||||
panel: vscode.WebviewPanel,
|
||||
extensionPath: string
|
||||
) {
|
||||
this._panel = panel;
|
||||
this._extensionPath = extensionPath;
|
||||
|
||||
// Set the webview's initial html content
|
||||
this._update();
|
||||
// Set the webview's initial html content
|
||||
this._update();
|
||||
|
||||
// Listen for when the panel is disposed
|
||||
// This happens when the user closes the panel or when the panel is closed programatically
|
||||
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
|
||||
// Listen for when the panel is disposed
|
||||
// This happens when the user closes the panel or when the panel is closed programatically
|
||||
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
|
||||
|
||||
// Update the content based on view changes
|
||||
this._panel.onDidChangeViewState(
|
||||
e => {
|
||||
if (this._panel.visible) {
|
||||
this._update();
|
||||
}
|
||||
},
|
||||
null,
|
||||
this._disposables
|
||||
);
|
||||
// Update the content based on view changes
|
||||
this._panel.onDidChangeViewState(e => {
|
||||
if (this._panel.visible) {
|
||||
this._update()
|
||||
}
|
||||
}, null, this._disposables);
|
||||
|
||||
// Handle messages from the webview
|
||||
this._panel.webview.onDidReceiveMessage(
|
||||
message => {
|
||||
switch (message.command) {
|
||||
case 'alert':
|
||||
vscode.window.showErrorMessage(message.text);
|
||||
return;
|
||||
}
|
||||
},
|
||||
null,
|
||||
this._disposables
|
||||
);
|
||||
}
|
||||
// Handle messages from the webview
|
||||
this._panel.webview.onDidReceiveMessage(message => {
|
||||
switch (message.command) {
|
||||
case 'alert':
|
||||
vscode.window.showErrorMessage(message.text);
|
||||
return;
|
||||
}
|
||||
}, null, this._disposables);
|
||||
}
|
||||
|
||||
public doRefactor() {
|
||||
// Send a message to the webview webview.
|
||||
// You can send any JSON serializable data.
|
||||
this._panel.webview.postMessage({ command: 'refactor' });
|
||||
}
|
||||
public doRefactor() {
|
||||
// Send a message to the webview webview.
|
||||
// You can send any JSON serializable data.
|
||||
this._panel.webview.postMessage({ command: 'refactor' });
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
CatCodingPanel.currentPanel = undefined;
|
||||
public dispose() {
|
||||
CatCodingPanel.currentPanel = undefined;
|
||||
|
||||
// Clean up our resources
|
||||
this._panel.dispose();
|
||||
// Clean up our resources
|
||||
this._panel.dispose();
|
||||
|
||||
while (this._disposables.length) {
|
||||
const x = this._disposables.pop();
|
||||
if (x) {
|
||||
x.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
while (this._disposables.length) {
|
||||
const x = this._disposables.pop();
|
||||
if (x) {
|
||||
x.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _update() {
|
||||
const z = 1 + 2;
|
||||
// Vary the webview's content based on where it is located in the editor.
|
||||
switch (this._panel.viewColumn) {
|
||||
case vscode.ViewColumn.Two:
|
||||
this._updateForCat('Compiling Cat');
|
||||
return;
|
||||
private _update() {
|
||||
|
||||
case vscode.ViewColumn.Three:
|
||||
this._updateForCat('Testing Cat');
|
||||
return;
|
||||
const z = 1 + 2;
|
||||
// Vary the webview's content based on where it is located in the editor.
|
||||
switch (this._panel.viewColumn) {
|
||||
case vscode.ViewColumn.Two:
|
||||
this._updateForCat('Compiling Cat');
|
||||
return;
|
||||
|
||||
case vscode.ViewColumn.One:
|
||||
default:
|
||||
this._updateForCat('Coding Cat');
|
||||
return;
|
||||
}
|
||||
}
|
||||
case vscode.ViewColumn.Three:
|
||||
this._updateForCat('Testing Cat');
|
||||
return;
|
||||
|
||||
private _updateForCat(catName: keyof typeof cats) {
|
||||
this._panel.title = catName;
|
||||
this._panel.webview.html = this._getHtmlForWebview(cats[catName]);
|
||||
}
|
||||
case vscode.ViewColumn.One:
|
||||
default:
|
||||
this._updateForCat('Coding Cat');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private _getHtmlForWebview(catGif: string) {
|
||||
// Local path to main script run in the webview
|
||||
const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'media', 'main.js'));
|
||||
private _updateForCat(catName: keyof typeof cats) {
|
||||
this._panel.title = catName;
|
||||
this._panel.webview.html = this._getHtmlForWebview(cats[catName]);
|
||||
}
|
||||
|
||||
// And the uri we use to load this script in the webview
|
||||
const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' });
|
||||
private _getHtmlForWebview(catGif: string) {
|
||||
|
||||
// Use a nonce to whitelist which scripts can be run
|
||||
const nonce = getNonce();
|
||||
// Local path to main script run in the webview
|
||||
const scriptPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, 'media', 'main.js'));
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
// And the uri we use to load this script in the webview
|
||||
const scriptUri = scriptPathOnDisk.with({ scheme: 'vscode-resource' });
|
||||
|
||||
// Use a nonce to whitelist which scripts can be run
|
||||
const nonce = getNonce();
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@ -189,14 +180,14 @@ class CatCodingPanel {
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNonce() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
let text = "";
|
||||
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
Reference in New Issue
Block a user