Merge remote-tracking branch 'origin/master' into alex/extension-host

This commit is contained in:
Alex Dima
2020-06-22 09:39:20 +02:00
233 changed files with 3438 additions and 2324 deletions
+2 -1
View File
@@ -519,7 +519,8 @@
"**/vs/workbench/services/**/common/**",
"**/vs/workbench/api/**/common/**",
"vscode-textmate",
"vscode-oniguruma"
"vscode-oniguruma",
"iconv-lite-umd"
]
},
{
+13
View File
@@ -221,6 +221,19 @@
"addLabel": "*caused-by-extension",
"comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/Microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!"
},
{
"type": "comment",
"name": "extCpp",
"allowUsers": [
"cleidigh",
"usernamehw",
"gjsjohnmurray",
"IllusionMH"
],
"action": "close",
"addLabel": "*caused-by-extension",
"comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/Microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!"
},
{
"type": "comment",
"name": "extTS",
@@ -15,7 +15,7 @@ jobs:
path: ./actions
- name: Install Actions
run: npm install --production --prefix ./actions
- name: "Run Classifier: Scraper"
- name: "Run Classifier: Monitor"
uses: ./actions/classifier-deep/monitor
with:
botName: vscode-triage-bot
@@ -45,5 +45,6 @@ jobs:
uses: ./actions/classifier-deep/apply/apply-labels
with:
configPath: classifier
allowLabels: "needs more info|new release"
appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}}
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
+8
View File
@@ -48,6 +48,14 @@ jobs:
mustNotMatch: "^We have written the needed data into your clipboard because it was too large to send\\. Please paste\\.$"
comment: "It looks like you're using the VS Code Issue Reporter but did not paste the text generated into the created issue. We've closed this issue, please open a new one containing the text we placed in your clipboard.\n\nHappy Coding!"
- name: Run Clipboard Labeler (Chinese)
uses: ./actions/regex-labeler
with:
appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}}
label: "invalid"
mustNotMatch: "^所需的数据太大,无法直接发送。我们已经将其写入剪贴板,请粘贴。$"
comment: "看起来您正在使用 VS Code 问题报告程序,但是没有将生成的文本粘贴到创建的问题中。我们将关闭这个问题,请使用剪贴板中的内容创建一个新的问题。\n\n祝您使用愉快!"
# source of truth in ./english-please.yml
- name: Run English Please
uses: ./actions/english-please
+5 -1
View File
@@ -13,7 +13,11 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v1
- name: Install dependencies
run: yarn install
run: yarn --frozen-lockfile
env:
CHILD_CONCURRENCY: 1
- uses: microsoft/RichCodeNavIndexer@master
with:
languages: typescript
repo-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
+1 -1
View File
@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "7.3.1"
target "8.3.3"
runtime "electron"
+3 -2
View File
@@ -16,7 +16,7 @@ const https = require("https");
const gulp = require("gulp");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const iconv = require("iconv-lite");
const iconv = require("iconv-lite-umd");
const NUMBER_OF_CONCURRENT_DOWNLOADS = 4;
function log(message, ...rest) {
fancyLog(ansiColors.green('[i18n]'), message, ...rest);
@@ -1175,9 +1175,10 @@ function createIslFile(originalFilePath, messages, language, innoSetup) {
});
const basename = path.basename(originalFilePath);
const filePath = `${basename}.${language.id}.isl`;
const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage);
return new File({
path: filePath,
contents: iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage)
contents: Buffer.from(encoded),
});
}
function encodeEntities(value) {
+3 -2
View File
@@ -15,7 +15,7 @@ import * as https from 'https';
import * as gulp from 'gulp';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
import * as iconv from 'iconv-lite';
import * as iconv from 'iconv-lite-umd';
const NUMBER_OF_CONCURRENT_DOWNLOADS = 4;
@@ -1339,10 +1339,11 @@ function createIslFile(originalFilePath: string, messages: Map<string>, language
const basename = path.basename(originalFilePath);
const filePath = `${basename}.${language.id}.isl`;
const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage);
return new File({
path: filePath,
contents: iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage)
contents: Buffer.from(encoded),
});
}
+1 -1
View File
@@ -38,7 +38,7 @@
"gulp-bom": "^1.0.0",
"gulp-sourcemaps": "^1.11.0",
"gulp-uglify": "^3.0.0",
"iconv-lite": "0.6.0",
"iconv-lite-umd": "0.6.3",
"mime": "^1.3.4",
"minimatch": "3.0.4",
"minimist": "^1.2.3",
+4 -11
View File
@@ -1415,12 +1415,10 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
iconv-lite@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.0.tgz#66a93b80df0bd05d2a43a7426296b7f91073f125"
integrity sha512-43ZpGYZ9QtuutX5l6WC1DSO8ane9N+Ct5qPLF2OV7vM9abM69gnAbVkh66ibaZd3aOGkoP1ZmringlKhLBkw2Q==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite-umd@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.3.tgz#61307cab8ac29939992d0724d3ab8799467f0e97"
integrity sha512-fQ/8XE8reiCZ6t+SX4tX6/tQdV4tThJZv5qtMe5Sk+IWsExz0S2Zd+GiBS5IEPgDxnsmiJSpH67+qzN3FT4lKw==
ignore@^5.1.1:
version "5.1.2"
@@ -2228,11 +2226,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea"
+6 -6
View File
@@ -6,7 +6,7 @@
"git": {
"name": "chromium",
"repositoryUrl": "https://chromium.googlesource.com/chromium/src",
"commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf"
"commitHash": "052d3b44972e6d94ef40054d46c150b7cdd7a5d8"
}
},
"licenseDetail": [
@@ -40,7 +40,7 @@
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
],
"isOnlyProductionDependency": true,
"version": "78.0.3904.130"
"version": "80.0.3987.165"
},
{
"component": {
@@ -48,11 +48,11 @@
"git": {
"name": "nodejs",
"repositoryUrl": "https://github.com/nodejs/node",
"commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a"
"commitHash": "42cce5a9d0fd905bf4ad7a2528c36572dfb8b5ad"
}
},
"isOnlyProductionDependency": true,
"version": "12.8.1"
"version": "12.13.0"
},
{
"component": {
@@ -60,12 +60,12 @@
"git": {
"name": "electron",
"repositoryUrl": "https://github.com/electron/electron",
"commitHash": "bc8fc0d406d32e4c02f3ec9f161deaacbe4f5989"
"commitHash": "87fd06bc96bce8f46ca05b8315657fd230bcac85"
}
},
"isOnlyProductionDependency": true,
"license": "MIT",
"version": "7.3.1"
"version": "8.3.3"
},
{
"component": {
+2 -2
View File
@@ -19,7 +19,7 @@
"git": {
"name": "textmate/c.tmbundle",
"repositoryUrl": "https://github.com/textmate/c.tmbundle",
"commitHash": "9aa365882274ca52f01722f3dbb169b9539a20ee"
"commitHash": "60daf83b9d45329524f7847a75e9298b3aae5805"
}
},
"licenseDetail": [
@@ -42,4 +42,4 @@
}
],
"version": 1
}
}
File diff suppressed because one or more lines are too long
@@ -76,17 +76,13 @@ function getCustomDataPathsInAllWorkspaces(): string[] {
function getCustomDataPathsFromAllExtensions(): string[] {
const dataPaths: string[] = [];
for (const extension of extensions.all) {
const contributes = extension.packageJSON && extension.packageJSON.contributes;
if (contributes && contributes.css && contributes.css.customData && Array.isArray(contributes.css.customData)) {
const relativePaths: string[] = contributes.css.customData;
relativePaths.forEach(rp => {
const customData = extension.packageJSON?.contributes?.css?.customData;
if (Array.isArray(customData)) {
for (const rp of customData) {
dataPaths.push(joinPath(extension.extensionUri, rp).toString());
});
}
}
}
return dataPaths;
}
@@ -29,7 +29,7 @@ function parseCSSData(source: string): ICSSDataProvider {
}
return newCSSDataProvider({
version: 1,
version: rawData.version || 1,
properties: rawData.properties || [],
atDirectives: rawData.atDirectives || [],
pseudoClasses: rawData.pseudoClasses || [],
+1 -1
View File
@@ -1878,7 +1878,7 @@
"dependencies": {
"byline": "^5.0.0",
"file-type": "^7.2.0",
"iconv-lite": "0.6.0",
"iconv-lite-umd": "0.6.3",
"jschardet": "2.1.1",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0",
+19 -22
View File
@@ -8,15 +8,15 @@ import * as path from 'path';
import { Repository, GitResourceGroup } from './repository';
import { Model } from './model';
import { debounce } from './decorators';
import { filterEvent, dispose, anyEvent, fireEvent } from './util';
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource } from './util';
import { GitErrorCodes, Status } from './api/git';
type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void };
class GitIgnoreDecorationProvider implements DecorationProvider {
private static Decoration: Decoration = { priority: 3, color: new ThemeColor('gitDecoration.ignoredResourceForeground') };
readonly onDidChangeDecorations: Event<Uri[]>;
private queue = new Map<string, { repository: Repository; queue: Map<string, Callback>; }>();
private queue = new Map<string, { repository: Repository; queue: Map<string, PromiseSource<Decoration | undefined>>; }>();
private disposables: Disposable[] = [];
constructor(private model: Model) {
@@ -29,32 +29,29 @@ class GitIgnoreDecorationProvider implements DecorationProvider {
this.disposables.push(window.registerDecorationProvider(this));
}
provideDecoration(uri: Uri): Promise<Decoration | undefined> {
async provideDecoration(uri: Uri): Promise<Decoration | undefined> {
const repository = this.model.getRepository(uri);
if (!repository) {
return Promise.resolve(undefined);
return;
}
let queueItem = this.queue.get(repository.root);
if (!queueItem) {
queueItem = { repository, queue: new Map<string, Callback>() };
queueItem = { repository, queue: new Map<string, PromiseSource<Decoration | undefined>>() };
this.queue.set(repository.root, queueItem);
}
return new Promise<boolean>((resolve, reject) => {
queueItem!.queue.set(uri.fsPath, { resolve, reject });
let promiseSource = queueItem.queue.get(uri.fsPath);
if (!promiseSource) {
promiseSource = new PromiseSource();
queueItem!.queue.set(uri.fsPath, promiseSource);
this.checkIgnoreSoon();
}).then(ignored => {
if (ignored) {
return <Decoration>{
priority: 3,
color: new ThemeColor('gitDecoration.ignoredResourceForeground')
};
}
return undefined;
});
}
return await promiseSource.promise;
}
@debounce(500)
@@ -66,16 +63,16 @@ class GitIgnoreDecorationProvider implements DecorationProvider {
const paths = [...item.queue.keys()];
item.repository.checkIgnore(paths).then(ignoreSet => {
for (const [key, value] of item.queue.entries()) {
value.resolve(ignoreSet.has(key));
for (const [path, promiseSource] of item.queue.entries()) {
promiseSource.resolve(ignoreSet.has(path) ? GitIgnoreDecorationProvider.Decoration : undefined);
}
}, err => {
if (err.gitErrorCode !== GitErrorCodes.IsInSubmodule) {
console.error(err);
}
for (const [, value] of item.queue.entries()) {
value.reject(err);
for (const [, promiseSource] of item.queue.entries()) {
promiseSource.reject(err);
}
});
}
+1 -1
View File
@@ -9,7 +9,7 @@ import * as os from 'os';
import * as cp from 'child_process';
import * as which from 'which';
import { EventEmitter } from 'events';
import iconv = require('iconv-lite');
import * as iconv from 'iconv-lite-umd';
import * as filetype from 'file-type';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util';
import { CancellationToken, Progress, Uri } from 'vscode';
+37 -1
View File
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Disposable } from 'vscode';
import { Event, Disposable, EventEmitter } from 'vscode';
import { dirname, sep } from 'path';
import { Readable } from 'stream';
import { promises as fs, createReadStream } from 'fs';
@@ -400,3 +400,39 @@ export class Limiter<T> {
}
}
}
type Completion<T> = { success: true, value: T } | { success: false, err: any };
export class PromiseSource<T> {
private _onDidComplete = new EventEmitter<Completion<T>>();
private _promise: Promise<T> | undefined;
get promise(): Promise<T> {
if (this._promise) {
return this._promise;
}
return eventToPromise(this._onDidComplete.event).then(completion => {
if (completion.success) {
return completion.value;
} else {
throw completion.err;
}
});
}
resolve(value: T): void {
if (!this._promise) {
this._promise = Promise.resolve(value);
this._onDidComplete.fire({ success: true, value });
}
}
reject(err: any): void {
if (!this._promise) {
this._promise = Promise.reject(err);
this._onDidComplete.fire({ success: false, err });
}
}
}
+5 -7
View File
@@ -425,12 +425,10 @@ https-proxy-agent@^2.2.1:
agent-base "^4.3.0"
debug "^3.1.0"
iconv-lite@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.0.tgz#66a93b80df0bd05d2a43a7426296b7f91073f125"
integrity sha512-43ZpGYZ9QtuutX5l6WC1DSO8ane9N+Ct5qPLF2OV7vM9abM69gnAbVkh66ibaZd3aOGkoP1ZmringlKhLBkw2Q==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite-umd@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.3.tgz#61307cab8ac29939992d0724d3ab8799467f0e97"
integrity sha512-fQ/8XE8reiCZ6t+SX4tX6/tQdV4tThJZv5qtMe5Sk+IWsExz0S2Zd+GiBS5IEPgDxnsmiJSpH67+qzN3FT4lKw==
inflight@^1.0.4:
version "1.0.6"
@@ -748,7 +746,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -10,7 +10,7 @@
"bin": {
"vscode-json-languageserver": "./bin/vscode-json-languageserver"
},
"main": "./out/jsonServerMain",
"main": "./out/node/jsonServerMain",
"dependencies": {
"jsonc-parser": "^2.2.1",
"request-light": "^0.3.0",
@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
const path = require('path');
const clientConfig = withDefaults({
target: 'webworker',
context: __dirname,
entry: {
extension: './src/npmBrowserMain.ts'
},
output: {
filename: 'npmBrowserMain.js'
},
performance: {
hints: false
},
resolve: {
alias: {
'vscode-nls': path.resolve(__dirname, '../../build/polyfills/vscode-nls.js')
}
},
node: {
'child_process': 'empty'
}
});
clientConfig.module.rules[0].use.shift(); // remove nls loader
module.exports = clientConfig;
+2 -4
View File
@@ -14,12 +14,10 @@ const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/main.ts',
extension: './src/npmMain.ts',
},
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist'),
libraryTarget: 'commonjs',
filename: 'npmMain.js',
},
resolve: {
mainFields: ['module', 'main'],
+8 -7
View File
@@ -20,14 +20,15 @@
"dependencies": {
"jsonc-parser": "^2.2.1",
"minimatch": "^3.0.4",
"request-light": "^0.2.5",
"request-light": "^0.4.0",
"vscode-nls": "^4.1.1"
},
"devDependencies": {
"@types/minimatch": "^3.0.3",
"@types/node": "^12.11.7"
},
"main": "./out/main",
"main": "./out/npmMain",
"browser": "./dist/npmBrowserMain",
"activationEvents": [
"onCommand:workbench.action.tasks.runTask",
"onCommand:npm.runScriptFromFolder",
@@ -181,12 +182,12 @@
}
],
"explorer/context": [
{
"when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder",
{
"when": "config.npm.enableRunFromFolder && explorerViewletVisible && explorerResourceIsFolder",
"command": "npm.runScriptFromFolder",
"group": "2_workspace"
}
]
"group": "2_workspace"
}
]
},
"configuration": {
"id": "npm",
@@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { MarkdownString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { IJSONContribution, ISuggestionsCollector } from './jsonContributions';
import { XHRRequest } from 'request-light';
import { Location } from 'jsonc-parser';
import { textToMarkedString } from './markedTextUtil';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
@@ -181,13 +180,15 @@ export class BowerJSONContribution implements IJSONContribution {
});
}
public getInfoContribution(_resource: string, location: Location): Thenable<MarkedString[] | null> | null {
public getInfoContribution(_resource: string, location: Location): Thenable<MarkdownString[] | null> | null {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']))) {
const pack = location.path[location.path.length - 1];
if (typeof pack === 'string') {
return this.getInfo(pack).then(documentation => {
if (documentation) {
return [textToMarkedString(documentation)];
const str = new MarkdownString();
str.appendText(documentation);
return [str];
}
return null;
});
@@ -30,8 +30,8 @@ export interface IJSONContribution {
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem | null> | null;
}
export function addJSONProviders(xhr: XHRRequest): Disposable {
const contributions = [new PackageJSONContribution(xhr), new BowerJSONContribution(xhr)];
export function addJSONProviders(xhr: XHRRequest, canRunNPM: boolean): Disposable {
const contributions = [new PackageJSONContribution(xhr, canRunNPM), new BowerJSONContribution(xhr)];
const subscriptions: Disposable[] = [];
contributions.forEach(contribution => {
const selector = contribution.getDocumentSelector();
@@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode';
import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace, MarkdownString } from 'vscode';
import { IJSONContribution, ISuggestionsCollector } from './jsonContributions';
import { XHRRequest } from 'request-light';
import { Location } from 'jsonc-parser';
import { textToMarkedString } from './markedTextUtil';
import * as cp from 'child_process';
import * as nls from 'vscode-nls';
@@ -28,14 +27,12 @@ export class PackageJSONContribution implements IJSONContribution {
'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench'];
private knownScopes = ['@types', '@angular', '@babel', '@nuxtjs', '@vue', '@bazel'];
private xhr: XHRRequest;
public getDocumentSelector(): DocumentSelector {
return [{ language: 'json', scheme: '*', pattern: '**/package.json' }];
}
public constructor(xhr: XHRRequest) {
this.xhr = xhr;
public constructor(private xhr: XHRRequest, private canRunNPM: boolean) {
}
public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable<any> {
@@ -191,23 +188,23 @@ export class PackageJSONContribution implements IJSONContribution {
const currentKey = location.path[location.path.length - 1];
if (typeof currentKey === 'string') {
const info = await this.fetchPackageInfo(currentKey);
if (info && info.distTagsLatest) {
if (info && info.version) {
let name = JSON.stringify(info.distTagsLatest);
let name = JSON.stringify(info.version);
let proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
proposal.documentation = localize('json.npm.latestversion', 'The currently latest version of the package');
result.add(proposal);
name = JSON.stringify('^' + info.distTagsLatest);
name = JSON.stringify('^' + info.version);
proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
proposal.documentation = localize('json.npm.majorversion', 'Matches the most recent major version (1.x.x)');
result.add(proposal);
name = JSON.stringify('~' + info.distTagsLatest);
name = JSON.stringify('~' + info.version);
proposal = new CompletionItem(name);
proposal.kind = CompletionItemKind.Property;
proposal.insertText = name;
@@ -219,14 +216,27 @@ export class PackageJSONContribution implements IJSONContribution {
return null;
}
private getDocumentation(description: string | undefined, version: string | undefined, homepage: string | undefined): MarkdownString {
const str = new MarkdownString();
if (description) {
str.appendText(description);
}
if (version) {
str.appendText('\n\n');
str.appendText(localize('json.npm.version.hover', 'Latest version: {0}', version));
}
if (homepage) {
str.appendText('\n\n');
str.appendText(homepage);
}
return str;
}
public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem | null> | null {
if (item.kind === CompletionItemKind.Property && item.documentation === '') {
return this.getInfo(item.label).then(infos => {
if (infos.length > 0) {
item.documentation = infos[0];
if (infos.length > 1) {
item.detail = infos[1];
}
if (item.kind === CompletionItemKind.Property && !item.documentation) {
return this.fetchPackageInfo(item.label).then(info => {
if (info) {
item.documentation = this.getDocumentation(info.description, info.version, info.homepage);
return item;
}
return null;
@@ -235,21 +245,11 @@ export class PackageJSONContribution implements IJSONContribution {
return null;
}
private async getInfo(pack: string): Promise<string[]> {
let info = await this.fetchPackageInfo(pack);
if (info) {
const result: string[] = [];
result.push(info.description || '');
result.push(info.distTagsLatest ? localize('json.npm.version.hover', 'Latest version: {0}', info.distTagsLatest) : '');
result.push(info.homepage || '');
return result;
}
return [];
}
private async fetchPackageInfo(pack: string): Promise<ViewPackageInfo | undefined> {
let info = await this.npmView(pack);
let info: ViewPackageInfo | undefined;
if (this.canRunNPM) {
info = await this.npmView(pack);
}
if (!info) {
info = await this.npmjsView(pack);
}
@@ -259,14 +259,14 @@ export class PackageJSONContribution implements IJSONContribution {
private npmView(pack: string): Promise<ViewPackageInfo | undefined> {
return new Promise((resolve, _reject) => {
const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage';
const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage version';
cp.exec(command, (error, stdout) => {
if (!error) {
try {
const content = JSON.parse(stdout);
resolve({
description: content['description'],
distTagsLatest: content['dist-tags.latest'],
version: content['dist-tags.latest'] || content['version'],
homepage: content['homepage']
});
return;
@@ -280,22 +280,20 @@ export class PackageJSONContribution implements IJSONContribution {
}
private async npmjsView(pack: string): Promise<ViewPackageInfo | undefined> {
const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@');
const queryUrl = 'https://api.npms.io/v2/package/' + encodeURIComponent(pack);
try {
const success = await this.xhr({
url: queryUrl,
agent: USER_AGENT
});
const obj = JSON.parse(success.responseText);
if (obj) {
const latest = obj && obj['dist-tags'] && obj['dist-tags']['latest'];
if (latest) {
return {
description: obj.description || '',
distTagsLatest: latest,
homepage: obj.homepage || ''
};
}
const metadata = obj?.collected?.metadata;
if (metadata) {
return {
description: metadata.description || '',
version: metadata.version,
homepage: metadata.links?.homepage || ''
};
}
}
catch (e) {
@@ -308,9 +306,9 @@ export class PackageJSONContribution implements IJSONContribution {
if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) {
const pack = location.path[location.path.length - 1];
if (typeof pack === 'string') {
return this.getInfo(pack).then(infos => {
if (infos.length) {
return [infos.map(textToMarkedString).join('\n\n')];
return this.fetchPackageInfo(pack).then(info => {
if (info) {
return [this.getDocumentation(info.description, info.version, info.homepage)];
}
return null;
});
@@ -339,7 +337,7 @@ export class PackageJSONContribution implements IJSONContribution {
proposal.kind = CompletionItemKind.Property;
proposal.insertText = insertText;
proposal.filterText = JSON.stringify(name);
proposal.documentation = pack.description || '';
proposal.documentation = this.getDocumentation(pack.description, pack.version, pack?.links?.homepage);
collector.add(proposal);
}
}
@@ -349,10 +347,11 @@ interface SearchPackageInfo {
name: string;
description?: string;
version?: string;
links?: { homepage?: string; };
}
interface ViewPackageInfo {
description: string;
distTagsLatest?: string;
version?: string;
homepage?: string;
}
@@ -3,8 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MarkedString } from 'vscode';
import * as httpRequest from 'request-light';
import * as vscode from 'vscode';
import { addJSONProviders } from './features/jsonContributions';
export function textToMarkedString(text: string): MarkedString {
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
}
export async function activate(context: vscode.ExtensionContext): Promise<void> {
context.subscriptions.push(addJSONProviders(httpRequest.xhr, false));
}
export function deactivate(): void {
}
@@ -14,13 +14,19 @@ import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHov
let treeDataProvider: NpmScriptsTreeDataProvider | undefined;
export async function activate(context: vscode.ExtensionContext): Promise<void> {
registerTaskProvider(context);
treeDataProvider = registerExplorer(context);
registerHoverProvider(context);
configureHttpRequest();
let d = vscode.workspace.onDidChangeConfiguration((e) => {
configureHttpRequest();
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.proxyStrictSSL')) {
configureHttpRequest();
}
}));
const canRunNPM = canRunNpmInCurrentWorkspace();
context.subscriptions.push(addJSONProviders(httpRequest.xhr, canRunNPM));
treeDataProvider = registerExplorer(context);
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect')) {
invalidateTasksCache();
if (treeDataProvider) {
@@ -32,15 +38,12 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
treeDataProvider.refresh();
}
}
});
context.subscriptions.push(d);
}));
registerTaskProvider(context);
registerHoverProvider(context);
d = vscode.workspace.onDidChangeTextDocument((e) => {
invalidateHoverScriptsCache(e.document);
});
context.subscriptions.push(d);
context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript));
context.subscriptions.push(addJSONProviders(httpRequest.xhr));
if (await hasPackageJson()) {
vscode.commands.executeCommand('setContext', 'npm:showScriptExplorer', true);
@@ -49,6 +52,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromFolder', selectAndRunScriptFromFolder));
}
function canRunNpmInCurrentWorkspace() {
if (vscode.workspace.workspaceFolders) {
return vscode.workspace.workspaceFolders.some(f => f.uri.scheme === 'file');
}
return false;
}
function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined {
function invalidateScriptCaches() {
+3
View File
@@ -32,6 +32,9 @@ export class NpmScriptHoverProvider implements HoverProvider {
constructor(context: ExtensionContext) {
context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this));
context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this));
context.subscriptions.push(workspace.onDidChangeTextDocument((e) => {
invalidateHoverScriptsCache(e.document);
}));
}
public provideHover(document: TextDocument, position: Position, _token: CancellationToken): ProviderResult<Hover> {
+12 -7
View File
@@ -71,7 +71,7 @@ http-proxy-agent@^2.1.0:
agent-base "4"
debug "3.1.0"
https-proxy-agent@^2.2.3:
https-proxy-agent@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
@@ -96,16 +96,21 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
request-light@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746"
integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==
request-light@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.4.0.tgz#c6b91ef00b18cb0de75d2127e55b3a2c9f7f90f9"
integrity sha512-fimzjIVw506FBZLspTAXHdpvgvQebyjpNyLRd0e6drPPRq7gcrROeGWRyF81wLqFg5ijPgnOQbmfck5wdTqpSA==
dependencies:
http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.3"
vscode-nls "^4.1.1"
https-proxy-agent "^2.2.4"
vscode-nls "^4.1.2"
vscode-nls@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
vscode-nls@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
+1 -1
View File
@@ -6,7 +6,7 @@
"git": {
"name": "Microsoft/vscode-mssql",
"repositoryUrl": "https://github.com/Microsoft/vscode-mssql",
"commitHash": "37a22725186b5b481b2882a78c7b9fe024c13946"
"commitHash": "750d30dc48c4c0317b63bb5f1ed3e71487bb84a1"
}
},
"license": "MIT",
+4 -4
View File
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/Microsoft/vscode-mssql/commit/37a22725186b5b481b2882a78c7b9fe024c13946",
"version": "https://github.com/Microsoft/vscode-mssql/commit/750d30dc48c4c0317b63bb5f1ed3e71487bb84a1",
"name": "SQL",
"scopeName": "source.sql",
"patterns": [
@@ -404,7 +404,7 @@
}
},
"comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.",
"match": "(N)?(')(?:[^'\\\\]|\\\\.)*(')",
"match": "(N)?(')[^']*(')",
"name": "string.quoted.single.sql"
},
{
@@ -437,7 +437,7 @@
}
},
"comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.",
"match": "(`)(?:[^`\\\\]|\\\\.)*(`)",
"match": "(`)[^`\\\\]*(`)",
"name": "string.quoted.other.backtick.sql"
},
{
@@ -470,7 +470,7 @@
}
},
"comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.",
"match": "(\")(?:[^\"#\\\\]|\\\\.)*(\")",
"match": "(\")[^\"#]*(\")",
"name": "string.quoted.double.sql"
},
{
@@ -569,7 +569,7 @@ suite('notebook undo redo', () => {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
});
test('execute and then undo redo', async function () {
test.skip('execute and then undo redo', async function () {
const resource = vscode.Uri.file(join(vscode.workspace.rootPath || '', './first.vsctestnb'));
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
+4 -6
View File
@@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.47.0",
"distro": "9521a559bbdc4e0fb1de84f14deb16cf56e7fdfa",
"distro": "6eb887883773e6b33879837bdf8dda3340a9fa75",
"author": {
"name": "Microsoft Corporation"
},
@@ -41,7 +41,7 @@
"graceful-fs": "4.2.3",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"iconv-lite": "0.6.0",
"iconv-lite-umd": "0.6.3",
"jschardet": "2.1.1",
"keytar": "^5.5.0",
"minimist": "^1.2.5",
@@ -49,7 +49,7 @@
"native-keymap": "2.1.2",
"native-watchdog": "1.3.0",
"node-pty": "0.10.0-beta8",
"semver-umd": "^5.5.6",
"semver-umd": "^5.5.7",
"spdlog": "^0.11.1",
"sudo-prompt": "9.1.1",
"v8-inspect-profiler": "^0.0.20",
@@ -74,7 +74,6 @@
"@types/debug": "^4.1.5",
"@types/graceful-fs": "4.1.2",
"@types/http-proxy-agent": "^2.0.1",
"@types/iconv-lite": "0.0.1",
"@types/keytar": "^4.4.0",
"@types/minimist": "^1.2.0",
"@types/mocha": "2.2.39",
@@ -94,12 +93,11 @@
"asar": "^0.14.0",
"chromium-pickle-js": "^0.2.0",
"copy-webpack-plugin": "^4.5.2",
"coveralls": "^2.11.11",
"cson-parser": "^1.3.3",
"css-loader": "^3.2.0",
"debounce": "^1.0.0",
"deemon": "^1.4.0",
"electron": "7.3.1",
"electron": "8.3.3",
"eslint": "6.8.0",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",
+2 -2
View File
@@ -8,12 +8,12 @@
"graceful-fs": "4.2.3",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"iconv-lite": "0.6.0",
"iconv-lite-umd": "0.6.3",
"jschardet": "2.1.1",
"minimist": "^1.2.5",
"native-watchdog": "1.3.0",
"node-pty": "0.10.0-beta8",
"semver-umd": "^5.5.6",
"semver-umd": "^5.5.7",
"spdlog": "^0.11.1",
"vscode-nsfw": "1.2.8",
"vscode-oniguruma": "1.3.1",
+3 -1
View File
@@ -2,7 +2,9 @@
"name": "vscode-web",
"version": "0.0.0",
"dependencies": {
"semver-umd": "^5.5.6",
"semver-umd": "^5.5.7",
"iconv-lite-umd": "0.6.3",
"jschardet": "2.1.1",
"vscode-oniguruma": "1.3.1",
"vscode-textmate": "5.1.1",
"xterm": "4.7.0-beta.3",
+14 -4
View File
@@ -2,10 +2,20 @@
# yarn lockfile v1
semver-umd@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228"
integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw==
iconv-lite-umd@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.3.tgz#61307cab8ac29939992d0724d3ab8799467f0e97"
integrity sha512-fQ/8XE8reiCZ6t+SX4tX6/tQdV4tThJZv5qtMe5Sk+IWsExz0S2Zd+GiBS5IEPgDxnsmiJSpH67+qzN3FT4lKw==
jschardet@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.1.1.tgz#af6f8fd0b3b0f5d46a8fd9614a4fce490575c184"
integrity sha512-pA5qG9Zwm8CBpGlK/lo2GE9jPxwqRgMV7Lzc/1iaPccw6v4Rhj8Zg2BTyrdmHmxlJojnbLupLeRnaPLsq03x6Q==
semver-umd@^5.5.7:
version "5.5.7"
resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.7.tgz#966beb5e96c7da6fbf09c3da14c2872d6836c528"
integrity sha512-XgjPNlD0J6aIc8xoTN6GQGwWc2Xg0kq8NzrqMVuKG/4Arl6ab1F8+Am5Y/XKKCR+FceFr2yN/Uv5ZJBhRyRqKg==
vscode-oniguruma@1.3.1:
version "1.3.1"
+8 -15
View File
@@ -176,12 +176,10 @@ https-proxy-agent@^2.2.3:
agent-base "^4.3.0"
debug "^3.1.0"
iconv-lite@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.0.tgz#66a93b80df0bd05d2a43a7426296b7f91073f125"
integrity sha512-43ZpGYZ9QtuutX5l6WC1DSO8ane9N+Ct5qPLF2OV7vM9abM69gnAbVkh66ibaZd3aOGkoP1ZmringlKhLBkw2Q==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite-umd@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.3.tgz#61307cab8ac29939992d0724d3ab8799467f0e97"
integrity sha512-fQ/8XE8reiCZ6t+SX4tX6/tQdV4tThJZv5qtMe5Sk+IWsExz0S2Zd+GiBS5IEPgDxnsmiJSpH67+qzN3FT4lKw==
ip@^1.1.5:
version "1.1.5"
@@ -300,15 +298,10 @@ readdirp@~3.2.0:
dependencies:
picomatch "^2.0.4"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver-umd@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.6.tgz#1d185bbd2caec825c564b54907cd09e14083f228"
integrity sha512-6ARYXVi4Y4VO5HfyCjT/6xyykBtJwEXSGQ8ON4UPQSFOjZUDsbAE0J614QcBBsLTTyQMEqvsXN804vAqpydjzw==
semver-umd@^5.5.7:
version "5.5.7"
resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.7.tgz#966beb5e96c7da6fbf09c3da14c2872d6836c528"
integrity sha512-XgjPNlD0J6aIc8xoTN6GQGwWc2Xg0kq8NzrqMVuKG/4Arl6ab1F8+Am5Y/XKKCR+FceFr2yN/Uv5ZJBhRyRqKg==
semver@^5.3.0:
version "5.6.0"
+1
View File
@@ -24,6 +24,7 @@ parts:
plugin: dump
source: .
stage-packages:
- ibus-gtk3
- fcitx-frontend-gtk3
- gvfs-libs
- libasound2
+39 -40
View File
@@ -6,7 +6,6 @@
*--------------------------------------------------------------------------------------------*/
// @ts-check
/** @typedef {import('../../src/vs/workbench/workbench.web.api').IWorkbenchConstructionOptions} WebConfiguration **/
const http = require('http');
const url = require('url');
@@ -62,15 +61,34 @@ const exists = (path) => util.promisify(fs.exists)(path);
const readFile = (path) => util.promisify(fs.readFile)(path);
const CharCode_PC = '%'.charCodeAt(0);
function toStaticExtensionUri(path) {
return { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${path}` };
}
async function initialize() {
const extensionFolders = await util.promisify(fs.readdir)(EXTENSIONS_ROOT);
const staticExtensions = [];
const builtinExtensions = [];
const webpackConfigs = [];
await Promise.all(extensionFolders.map(async extensionFolder => {
const packageJSONPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.json');
const children = await util.promisify(fs.readdir)(EXTENSIONS_ROOT, { withFileTypes: true });
const folders = children.filter(c => !c.isFile());
await Promise.all(folders.map(async folder => {
const folderName = folder.name;
const extensionPath = path.join(EXTENSIONS_ROOT, folderName);
let children = [];
try {
children = await util.promisify(fs.readdir)(extensionPath);
} catch (error) {
console.log(error);
return;
}
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
const readmeUrl = readme ? toStaticExtensionUri(path.join(extensionPath, readme)) : undefined;
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
const changelogUrl = changelog ? toStaticExtensionUri(path.join(extensionPath, changelog)) : undefined;
const packageJSONPath = path.join(EXTENSIONS_ROOT, folderName, 'package.json');
if (await exists(packageJSONPath)) {
try {
const packageJSON = JSON.parse((await readFile(packageJSONPath)).toString());
@@ -82,7 +100,7 @@ async function initialize() {
packageJSON.main = packageJSON.browser;
const webpackConfigLocations = await util.promisify(glob)(
path.join(EXTENSIONS_ROOT, extensionFolder, '**', 'extension-browser.webpack.config.js'),
path.join(EXTENSIONS_ROOT, folderName, '**', 'extension-browser.webpack.config.js'),
{ ignore: ['**/node_modules'] }
);
@@ -99,31 +117,16 @@ async function initialize() {
}
}
const packageNlsPath = path.join(EXTENSIONS_ROOT, extensionFolder, 'package.nls.json');
if (await exists(packageNlsPath)) {
const packageNls = JSON.parse((await readFile(packageNlsPath)).toString());
const translate = (obj) => {
for (let key in obj) {
const val = obj[key];
if (Array.isArray(val)) {
val.forEach(translate);
} else if (val && typeof val === 'object') {
translate(val);
} else if (typeof val === 'string' && val.charCodeAt(0) === CharCode_PC && val.charCodeAt(val.length - 1) === CharCode_PC) {
const translated = packageNls[val.substr(1, val.length - 2)];
if (translated) {
obj[key] = translated;
}
}
}
};
translate(packageJSON);
}
packageJSON.extensionKind = ['web']; // enable for Web
staticExtensions.push({
const packageNLSPath = path.join(folderName, 'package.nls.json');
const packageNLSExists = await exists(path.join(EXTENSIONS_ROOT, packageNLSPath));
builtinExtensions.push({
packageJSON,
extensionLocation: { scheme: SCHEME, authority: AUTHORITY, path: `/static-extension/${extensionFolder}` },
isBuiltin: true
location: toStaticExtensionUri(folderName),
packageNLSUrl: packageNLSExists ? toStaticExtensionUri(packageNLSPath) : undefined,
readmeUrl,
changelogUrl
});
} catch (e) {
console.log(e);
@@ -139,7 +142,7 @@ async function initialize() {
reject();
} else {
console.log(stats.toString());
resolve(staticExtensions);
resolve(builtinExtensions);
}
});
} else {
@@ -149,14 +152,14 @@ async function initialize() {
reject();
} else {
console.log(stats.toString());
resolve(staticExtensions);
resolve(builtinExtensions);
}
});
}
});
}
const staticExtensionsPromise = initialize();
const builtinExtensionsPromise = initialize();
const mapCallbackUriToRequestId = new Map();
@@ -262,14 +265,9 @@ async function handleRoot(req, res) {
}
}
const staticExtensions = await staticExtensionsPromise;
/** @type {WebConfiguration} */
const webConfig = {
staticExtensions: staticExtensions,
};
const builtinExtensions = await builtinExtensionsPromise;
const webConfigJSON = escapeAttribute(JSON.stringify({
...webConfig,
folderUri: ghPath
? { scheme: 'github', authority: 'HEAD', path: ghPath }
: { scheme: 'memfs', path: `/sample-folder` },
@@ -277,6 +275,7 @@ async function handleRoot(req, res) {
const data = (await util.promisify(fs.readFile)(WEB_MAIN)).toString()
.replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => webConfigJSON) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied
.replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(builtinExtensions)))
.replace('{{WEBVIEW_ENDPOINT}}', '')
.replace('{{REMOTE_USER_DATA_URI}}', '');
+4 -12
View File
@@ -15,7 +15,7 @@ VSCODE_PATH="$(dirname "$(dirname "$(realpath "$0")")")"
ELECTRON="$VSCODE_PATH/$NAME.exe"
if grep -qi Microsoft /proc/version; then
# in a wsl shell
WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft.*|([0-9]+).([0-9]+).([0-9]+)-microsoft-standard.*|.*/\1\2\3\4/')
WSL_BUILD=$(uname -r | sed -E 's/^[0-9.]+-([0-9]+)-Microsoft.*|([0-9]+).([0-9]+).([0-9]+)-microsoft.*|.*/\1\2\3\4/')
if [ -z "$WSL_BUILD" ]; then
WSL_BUILD=0
fi
@@ -30,17 +30,9 @@ if grep -qi Microsoft /proc/version; then
# use the Remote WSL extension if installed
WSL_EXT_ID="ms-vscode-remote.remote-wsl"
if [ $WSL_BUILD -ge 41955 -a $WSL_BUILD -lt 41959 ]; then
# WSL2 workaround for https://github.com/microsoft/WSL/issues/4337
CWD="$(pwd)"
cd "$VSCODE_PATH"
cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >%TEMP%\\remote-wsl-loc.txt"
WSL_EXT_WLOC=$(cmd.exe /C type %TEMP%\\remote-wsl-loc.txt)
cd "$CWD"
else
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null
WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt)
fi
ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt 2>/dev/null
WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt)
if [ -n "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh
+2 -2
View File
@@ -43,8 +43,8 @@ if %errorlevel% neq 0 exit /b %errorlevel%
:: Tests in the extension host
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
:: call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-notebook-tests\test --enable-proposed-api=vscode.vscode-notebook-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-notebook-tests --extensionTestsPath=%~dp0\..\extensions\vscode-notebook-tests\out --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
:: if %errorlevel% neq 0 exit /b %errorlevel%
call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-api-tests\testWorkspace --enable-proposed-api=vscode.vscode-api-tests --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disable-telemetry --crash-reporter-directory=%VSCODECRASHDIR% --no-cached-data --disable-updates --disable-extensions --user-data-dir=%VSCODEUSERDATADIR%
if %errorlevel% neq 0 exit /b %errorlevel%
-1
View File
@@ -39,7 +39,6 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = {
const UTF8 = 'utf8';
export async function resolveTerminalEncoding(verbose?: boolean): Promise<string> {
let rawEncodingPromise: Promise<string>;
@@ -9,9 +9,10 @@ import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_
export function registerContextMenuListener(): void {
ipcMain.on(CONTEXT_MENU_CHANNEL, (event: IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => {
const menu = createMenu(event, onClickChannel, items);
const window = BrowserWindow.fromWebContents(event.sender);
menu.popup({
window: BrowserWindow.fromWebContents(event.sender),
window: window ? window : undefined,
x: options ? options.x : undefined,
y: options ? options.y : undefined,
positioningItem: options ? options.positioningItem : undefined,
@@ -13,6 +13,9 @@
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
<!-- Builtin Extensions (running out of sources) -->
<meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="/manifest.json">
@@ -34,6 +37,8 @@
'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
'xterm-addon-webgl': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`,
'iconv-lite-umd': `${window.location.origin}/static/remote/web/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
'jschardet': `${window.location.origin}/static/remote/web/node_modules/jschardet/dist/jschardet.min.js`,
}
};
</script>
@@ -13,6 +13,9 @@
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
<!-- Builtin Extensions (running out of sources) -->
<meta id="vscode-workbench-builtin-extensions" data-settings="{{WORKBENCH_BUILTIN_EXTENSIONS}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="/manifest.json">
@@ -20,6 +23,8 @@
<!-- Prefetch to avoid waterfall -->
<link rel="prefetch" href="./static/node_modules/semver-umd/lib/semver-umd.js">
<link rel="prefetch" href="./static/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js">
<link rel="prefetch" href="./static/node_modules/jschardet/dist/jschardet.min.js">
</head>
<body aria-label="">
@@ -38,6 +43,8 @@
'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
'xterm-addon-webgl': `${window.location.origin}/static/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
'iconv-lite-umd': `${window.location.origin}/static/node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
'jschardet': `${window.location.origin}/static/node_modules/jschardet/dist/jschardet.min.js`,
}
};
</script>
+86 -149
View File
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as types from 'vs/base/common/types';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Command, EditorCommand, ICommandOptions, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { Command, EditorCommand, ICommandOptions, registerEditorCommand, MultiCommand, UndoCommand, RedoCommand, SelectAllCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controller/cursorColumnSelection';
import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from 'vs/editor/common/controller/cursorCommon';
@@ -20,7 +20,6 @@ import { Range } from 'vs/editor/common/core/range';
import { Handler, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
@@ -276,6 +275,48 @@ export namespace RevealLine_ {
};
}
abstract class EditorOrNativeTextInputCommand {
constructor(target: MultiCommand) {
// 1. handle case when focus is in editor.
target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
// Only if editor text focus (i.e. not if editor has widget focus).
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
this.runEditorCommand(accessor, focusedEditor, args);
return true;
}
return false;
});
// 2. handle case when focus is in some other `input` / `textarea`.
target.addImplementation(1000, (accessor: ServicesAccessor, args: any) => {
// Only if focused on an element that allows for entering text
const activeElement = <HTMLElement>document.activeElement;
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
this.runDOMCommand();
return true;
}
return false;
});
// 3. (default) handle case when focus is somewhere else.
target.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
// Redirecting to active editor
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
if (activeEditor) {
activeEditor.focus();
this.runEditorCommand(accessor, activeEditor, args);
return true;
}
return false;
});
}
public abstract runDOMCommand(): void;
public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void;
}
export namespace CoreNavigationCommands {
class BaseMoveToCommand extends CoreEditorCommand {
@@ -1594,25 +1635,32 @@ export namespace CoreNavigationCommands {
}
});
export const SelectAll: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
export const SelectAll = new class extends EditorOrNativeTextInputCommand {
constructor() {
super({
id: 'selectAll',
precondition: undefined
});
super(SelectAllCommand);
}
public runDOMCommand(): void {
document.execCommand('selectAll');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
const viewModel = editor._getViewModel();
if (!viewModel) {
// the editor has no view => has no cursors
return;
}
this.runCoreEditorCommand(viewModel, args);
}
public runCoreEditorCommand(viewModel: IViewModel, args: any): void {
viewModel.model.pushStackElement();
viewModel.setCursorStates(
args.source,
'keyboard',
CursorChangeReason.Explicit,
[
CursorMoveCommands.selectAll(viewModel, viewModel.getPrimaryCursorState())
]
);
}
});
}();
export const SetSelection: CoreEditorCommand = registerEditorCommand(new class extends CoreEditorCommand {
constructor() {
@@ -1655,97 +1703,6 @@ registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageUp.id, KeyM
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectDown.id, KeyMod.Shift | KeyCode.DownArrow);
registerColumnSelection(CoreNavigationCommands.CursorColumnSelectPageDown.id, KeyMod.Shift | KeyCode.PageDown);
/**
* A command that will:
* 1. invoke a command on the focused editor.
* 2. otherwise, invoke a browser built-in command on the `activeElement`.
* 3. otherwise, invoke a command on the workbench active editor.
*/
abstract class EditorOrNativeTextInputCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
// Only if editor text focus (i.e. not if editor has widget focus).
if (focusedEditor && focusedEditor.hasTextFocus()) {
return this.runEditorCommand(accessor, focusedEditor, args);
}
// Ignore this action when user is focused on an element that allows for entering text
const activeElement = <HTMLElement>document.activeElement;
if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
return this.runDOMCommand();
}
// Redirecting to active editor
const activeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor();
if (activeEditor) {
activeEditor.focus();
return this.runEditorCommand(accessor, activeEditor, args);
}
}
public abstract runDOMCommand(): void;
public abstract runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void;
}
class SelectAllCommand extends EditorOrNativeTextInputCommand {
constructor() {
super({
id: 'editor.action.selectAll',
precondition: EditorContextKeys.textInputFocus,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_A
},
menuOpts: [{
menuId: MenuId.MenubarSelectionMenu,
group: '1_basic',
title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"),
order: 1
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('selectAll', "Select All"),
order: 1
}]
});
}
public runDOMCommand(): void {
document.execCommand('selectAll');
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
args = args || {};
args.source = 'keyboard';
CoreNavigationCommands.SelectAll.runEditorCommand(accessor, editor, args);
}
}
class UndoCommand extends EditorOrNativeTextInputCommand {
public runDOMCommand(): void {
document.execCommand('undo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
editor.getModel().undo();
}
}
class RedoCommand extends EditorOrNativeTextInputCommand {
public runDOMCommand(): void {
document.execCommand('redo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
editor.getModel().redo();
}
}
function registerCommand<T extends Command>(command: T): T {
command.register();
return command;
@@ -1881,53 +1838,35 @@ export namespace CoreEditingCommands {
}
});
export const Undo: UndoCommand = registerCommand(new UndoCommand({
id: 'undo',
precondition: EditorContextKeys.writable,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z
},
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"),
order: 1
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('undo', "Undo"),
order: 1
}]
}));
export const Undo = new class extends EditorOrNativeTextInputCommand {
constructor() {
super(UndoCommand);
}
public runDOMCommand(): void {
document.execCommand('undo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
editor.getModel().undo();
}
}();
export const DefaultUndo: UndoCommand = registerCommand(new UndoCommand({ id: 'default:undo', precondition: EditorContextKeys.writable }));
export const Redo: RedoCommand = registerCommand(new RedoCommand({
id: 'redo',
precondition: EditorContextKeys.writable,
kbOpts: {
weight: CORE_WEIGHT,
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }
},
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"),
order: 2
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('redo', "Redo"),
order: 1
}]
}));
export const DefaultRedo: RedoCommand = registerCommand(new RedoCommand({ id: 'default:redo', precondition: EditorContextKeys.writable }));
export const Redo = new class extends EditorOrNativeTextInputCommand {
constructor() {
super(RedoCommand);
}
public runDOMCommand(): void {
document.execCommand('redo');
}
public runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void {
if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) {
return;
}
editor.getModel().redo();
}
}();
}
/**
@@ -1956,8 +1895,6 @@ class EditorHandlerCommand extends Command {
}
}
registerCommand(new SelectAllCommand());
function registerOverwritableCommand(handlerId: string, description?: ICommandHandlerDescription): void {
registerCommand(new EditorHandlerCommand('default:' + handlerId, handlerId));
registerCommand(new EditorHandlerCommand(handlerId, handlerId, description));
+140 -3
View File
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IPosition } from 'vs/base/browser/ui/contextview/contextview';
import { illegalArgument } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
@@ -17,11 +18,13 @@ import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IKeybindings, KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { withNullAsUndefined, assertType } from 'vs/base/common/types';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
export type ServicesAccessor = InstantiationServicesAccessor;
@@ -139,6 +142,66 @@ export abstract class Command {
//#endregion Command
//#region MultiplexingCommand
/**
* Potential override for a command.
*
* @return `true` if the command was successfully run. This stops other overrides from being executed.
*/
export type CommandImplementation = (accessor: ServicesAccessor, args: unknown) => boolean;
export class MultiCommand extends Command {
private readonly _implementations: [number, CommandImplementation][] = [];
/**
* A higher priority gets to be looked at first
*/
public addImplementation(priority: number, implementation: CommandImplementation): IDisposable {
this._implementations.push([priority, implementation]);
this._implementations.sort((a, b) => b[0] - a[0]);
return {
dispose: () => {
for (let i = 0; i < this._implementations.length; i++) {
if (this._implementations[i][1] === implementation) {
this._implementations.splice(i, 1);
return;
}
}
}
};
}
public runCommand(accessor: ServicesAccessor, args: any): void | Promise<void> {
for (const impl of this._implementations) {
if (impl[1](accessor, args)) {
return;
}
}
}
}
//#endregion
/**
* A command that delegates to another command's implementation.
*
* This lets different commands be registered but share the same implementation
*/
export class ProxyCommand extends Command {
constructor(
private readonly command: Command,
opts: ICommandOptions
) {
super(opts);
}
public runCommand(accessor: ServicesAccessor, args: any): void | Promise<void> {
return this.command.runCommand(accessor, args);
}
}
//#region EditorCommand
export interface IContributionCommandOptions<T> extends ICommandOptions {
@@ -379,8 +442,10 @@ export function registerEditorCommand<T extends EditorCommand>(editorCommand: T)
return editorCommand;
}
export function registerEditorAction(ctor: { new(): EditorAction; }): void {
EditorContributionRegistry.INSTANCE.registerEditorAction(new ctor());
export function registerEditorAction<T extends EditorAction>(ctor: { new(): T; }): T {
const action = new ctor();
EditorContributionRegistry.INSTANCE.registerEditorAction(action);
return action;
}
export function registerInstantiatedEditorAction(editorAction: EditorAction): void {
@@ -475,3 +540,75 @@ class EditorContributionRegistry {
}
Registry.add(Extensions.EditorCommonContributions, EditorContributionRegistry.INSTANCE);
function registerCommand<T extends Command>(command: T): T {
command.register();
return command;
}
export const UndoCommand = registerCommand(new MultiCommand({
id: 'undo',
precondition: undefined,
kbOpts: {
weight: KeybindingWeight.EditorCore,
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z
},
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"),
order: 1
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('undo', "Undo"),
order: 1
}]
}));
registerCommand(new ProxyCommand(UndoCommand, { id: 'default:undo', precondition: undefined }));
export const RedoCommand = registerCommand(new MultiCommand({
id: 'redo',
precondition: undefined,
kbOpts: {
weight: KeybindingWeight.EditorCore,
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z }
},
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '1_do',
title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"),
order: 2
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('redo', "Redo"),
order: 1
}]
}));
registerCommand(new ProxyCommand(RedoCommand, { id: 'default:redo', precondition: undefined }));
export const SelectAllCommand = registerCommand(new MultiCommand({
id: 'editor.action.selectAll',
precondition: undefined,
kbOpts: {
weight: KeybindingWeight.EditorCore,
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_A
},
menuOpts: [{
menuId: MenuId.MenubarSelectionMenu,
group: '1_basic',
title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"),
order: 1
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('selectAll', "Select All"),
order: 1
}]
}));
+18 -22
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { CoreEditorCommand, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents';
import { Position } from 'vs/editor/common/core/position';
@@ -35,8 +35,6 @@ export interface IMouseDispatchData {
}
export interface ICommandDelegate {
executeEditorCommand(editorCommand: CoreEditorCommand, args: any): void;
paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;
type(text: string): void;
replacePreviousChar(text: string, replaceCharCnt: number): void;
@@ -64,11 +62,6 @@ export class ViewController {
this.commandDelegate = commandDelegate;
}
private _execMouseCommand(editorCommand: CoreEditorCommand, args: any): void {
args.source = 'mouse';
this.commandDelegate.executeEditorCommand(editorCommand, args);
}
public paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {
this.commandDelegate.paste(text, pasteOnNewLine, multicursorText, mode);
}
@@ -94,7 +87,7 @@ export class ViewController {
}
public setSelection(modelSelection: Selection): void {
this.commandDelegate.executeEditorCommand(CoreNavigationCommands.SetSelection, {
CoreNavigationCommands.SetSelection.runCoreEditorCommand(this.viewModel, {
source: 'keyboard',
selection: modelSelection
});
@@ -214,22 +207,24 @@ export class ViewController {
private _usualArgs(viewPosition: Position) {
viewPosition = this._validateViewColumn(viewPosition);
return {
source: 'mouse',
position: this._convertViewToModelPosition(viewPosition),
viewPosition: viewPosition
};
}
public moveTo(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.MoveTo, this._usualArgs(viewPosition));
CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _moveToSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.MoveToSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _columnSelect(viewPosition: Position, mouseColumn: number, doColumnSelect: boolean): void {
viewPosition = this._validateViewColumn(viewPosition);
this._execMouseCommand(CoreNavigationCommands.ColumnSelect, {
CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(this.viewModel, {
source: 'mouse',
position: this._convertViewToModelPosition(viewPosition),
viewPosition: viewPosition,
mouseColumn: mouseColumn,
@@ -239,7 +234,8 @@ export class ViewController {
private _createCursor(viewPosition: Position, wholeLine: boolean): void {
viewPosition = this._validateViewColumn(viewPosition);
this._execMouseCommand(CoreNavigationCommands.CreateCursor, {
CoreNavigationCommands.CreateCursor.runCoreEditorCommand(this.viewModel, {
source: 'mouse',
position: this._convertViewToModelPosition(viewPosition),
viewPosition: viewPosition,
wholeLine: wholeLine
@@ -247,39 +243,39 @@ export class ViewController {
}
private _lastCursorMoveToSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LastCursorMoveToSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _wordSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.WordSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.WordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _wordSelectDrag(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.WordSelectDrag, this._usualArgs(viewPosition));
CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _lastCursorWordSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LastCursorWordSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.LastCursorWordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _lineSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LineSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.LineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _lineSelectDrag(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LineSelectDrag, this._usualArgs(viewPosition));
CoreNavigationCommands.LineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _lastCursorLineSelect(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelect, this._usualArgs(viewPosition));
CoreNavigationCommands.LastCursorLineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _lastCursorLineSelectDrag(viewPosition: Position): void {
this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelectDrag, this._usualArgs(viewPosition));
CoreNavigationCommands.LastCursorLineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition));
}
private _selectAll(): void {
this._execMouseCommand(CoreNavigationCommands.SelectAll, {});
CoreNavigationCommands.SelectAll.runCoreEditorCommand(this.viewModel, { source: 'mouse' });
}
// ----------------------
@@ -15,7 +15,6 @@ import { hash } from 'vs/base/common/hash';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
@@ -1552,9 +1551,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
let commandDelegate: ICommandDelegate;
if (this.isSimpleWidget) {
commandDelegate = {
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
editorCommand.runCoreEditorCommand(viewModel, args);
},
paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
this._paste('keyboard', text, pasteOnNewLine, multicursorText, mode);
},
@@ -1576,9 +1572,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
};
} else {
commandDelegate = {
executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => {
editorCommand.runCoreEditorCommand(viewModel, args);
},
paste: (text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => {
const payload: editorCommon.PastePayload = { text, pasteOnNewLine, multicursorText, mode };
this._commandService.executeCommand(editorCommon.Handler.Paste, payload);
+123 -155
View File
@@ -9,7 +9,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, IActionOptions, ICommandKeybindingsOptions, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { EditorAction, registerEditorAction, Command, MultiCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { MenuId } from 'vs/platform/actions/common/actions';
@@ -28,171 +28,111 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge);
// privileges to actually perform the action
const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
type ExecCommand = 'cut' | 'copy' | 'paste';
abstract class ExecCommandAction extends EditorAction {
private readonly browserCommand: ExecCommand;
constructor(browserCommand: ExecCommand, opts: IActionOptions) {
super(opts);
this.browserCommand = browserCommand;
}
public runCommand(accessor: ServicesAccessor, args: any): void {
let focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
// Only if editor text focus (i.e. not if editor has widget focus).
if (focusedEditor && focusedEditor.hasTextFocus()) {
focusedEditor.trigger('keyboard', this.id, args);
return;
}
document.execCommand(this.browserCommand);
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
editor.focus();
document.execCommand(this.browserCommand);
}
function registerCommand<T extends Command>(command: T): T {
command.register();
return command;
}
class ExecCommandCutAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions | undefined = {
export const CutAction = supportsCut ? registerCommand(new MultiCommand({
id: 'editor.action.clipboardCutAction',
precondition: undefined,
kbOpts: (
// Do not bind cut keybindings in the browser,
// since browsers do that for us and it avoids security prompts
platform.isNative ? {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind cut keybindings in the browser,
} : undefined
),
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"),
order: 1
}, {
menuId: MenuId.EditorContext,
group: CLIPBOARD_CONTEXT_MENU_GROUP,
title: nls.localize('actions.clipboard.cutLabel', "Cut"),
when: EditorContextKeys.writable,
order: 1,
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('actions.clipboard.cutLabel', "Cut"),
order: 1
}]
})) : undefined;
export const CopyAction = supportsCopy ? registerCommand(new MultiCommand({
id: 'editor.action.clipboardCopyAction',
precondition: undefined,
kbOpts: (
// Do not bind copy keybindings in the browser,
// since browsers do that for us and it avoids security prompts
if (!platform.isNative) {
kbOpts = undefined;
}
super('cut', {
id: 'editor.action.clipboardCutAction',
label: nls.localize('actions.clipboard.cutLabel', "Cut"),
alias: 'Cut',
precondition: EditorContextKeys.writable,
kbOpts: kbOpts,
contextMenuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 1
},
menuOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"),
order: 1
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard);
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
return;
}
super.run(accessor, editor);
}
}
class ExecCommandCopyAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions | undefined = {
platform.isNative ? {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind copy keybindings in the browser,
} : undefined
),
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"),
order: 2
}, {
menuId: MenuId.EditorContext,
group: CLIPBOARD_CONTEXT_MENU_GROUP,
title: nls.localize('actions.clipboard.copyLabel', "Copy"),
order: 2,
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('actions.clipboard.copyLabel', "Copy"),
order: 1
}]
})) : undefined;
export const PasteAction = supportsPaste ? registerCommand(new MultiCommand({
id: 'editor.action.clipboardPasteAction',
precondition: undefined,
kbOpts: (
// Do not bind paste keybindings in the browser,
// since browsers do that for us and it avoids security prompts
if (!platform.isNative) {
kbOpts = undefined;
}
super('copy', {
id: 'editor.action.clipboardCopyAction',
label: nls.localize('actions.clipboard.copyLabel', "Copy"),
alias: 'Copy',
precondition: undefined,
kbOpts: kbOpts,
contextMenuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 2
},
menuOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"),
order: 2
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!editor.hasModel()) {
return;
}
const emptySelectionClipboard = editor.getOption(EditorOption.emptySelectionClipboard);
if (!emptySelectionClipboard && editor.getSelection().isEmpty()) {
return;
}
super.run(accessor, editor);
}
}
class ExecCommandPasteAction extends ExecCommandAction {
constructor() {
let kbOpts: ICommandKeybindingsOptions | undefined = {
platform.isNative ? {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
linux: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind paste keybindings in the browser,
// since browsers do that for us and it avoids security prompts
if (!platform.isNative) {
kbOpts = undefined;
}
} : undefined
),
menuOpts: [{
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
order: 3
}, {
menuId: MenuId.EditorContext,
group: CLIPBOARD_CONTEXT_MENU_GROUP,
title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
when: EditorContextKeys.writable,
order: 3,
}, {
menuId: MenuId.CommandPalette,
group: '',
title: nls.localize('actions.clipboard.pasteLabel', "Paste"),
order: 1
}]
})) : undefined;
super('paste', {
id: 'editor.action.clipboardPasteAction',
label: nls.localize('actions.clipboard.pasteLabel', "Paste"),
alias: 'Paste',
precondition: EditorContextKeys.writable,
kbOpts: kbOpts,
contextMenuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 3
},
menuOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
order: 3
}
});
}
}
class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction {
constructor() {
super('copy', {
super({
id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction',
label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"),
alias: 'Copy With Syntax Highlighting',
@@ -217,20 +157,48 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
}
CopyOptions.forceCopyWithSyntaxHighlighting = true;
super.run(accessor, editor);
editor.focus();
document.execCommand('copy');
CopyOptions.forceCopyWithSyntaxHighlighting = false;
}
}
if (supportsCut) {
registerEditorAction(ExecCommandCutAction);
}
if (supportsCopy) {
registerEditorAction(ExecCommandCopyAction);
}
if (supportsPaste) {
registerEditorAction(ExecCommandPasteAction);
function registerExecCommandImpl(target: MultiCommand | undefined, browserCommand: 'cut' | 'copy' | 'paste'): void {
if (!target) {
return;
}
// 1. handle case when focus is in editor.
target.addImplementation(10000, (accessor: ServicesAccessor, args: any) => {
// Only if editor text focus (i.e. not if editor has widget focus).
const focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
if (focusedEditor && focusedEditor.hasTextFocus()) {
if (browserCommand === 'cut' || browserCommand === 'copy') {
// Do not execute if there is no selection and empty selection clipboard is off
const emptySelectionClipboard = focusedEditor.getOption(EditorOption.emptySelectionClipboard);
const selection = focusedEditor.getSelection();
if (selection && selection.isEmpty() && !emptySelectionClipboard) {
return true;
}
}
document.execCommand(browserCommand);
return true;
}
return false;
});
// 2. (default) handle case when focus is somewhere else.
target.addImplementation(0, (accessor: ServicesAccessor, args: any) => {
// Only if editor text focus (i.e. not if editor has widget focus).
document.execCommand(browserCommand);
return true;
});
}
registerExecCommandImpl(CutAction, 'cut');
registerExecCommandImpl(CopyAction, 'copy');
registerExecCommandImpl(PasteAction, 'paste');
if (supportsCopyWithSyntaxHighlighting) {
registerEditorAction(ExecCommandCopyWithSyntaxHighlightingAction);
}
+12 -12
View File
@@ -255,7 +255,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
// overwritten in subclass
}
protected _start(opts: IFindStartOptions): void {
protected async _start(opts: IFindStartOptions): Promise<void> {
this.disposeModel();
if (!this._editor.hasModel()) {
@@ -279,7 +279,7 @@ export class CommonFindController extends Disposable implements IEditorContribut
}
if (!stateChanges.searchString && opts.seedSearchStringFromGlobalClipboard) {
let selectionSearchString = this.getGlobalBufferTerm();
let selectionSearchString = await this.getGlobalBufferTerm();
if (selectionSearchString) {
stateChanges.searchString = selectionSearchString;
}
@@ -308,8 +308,8 @@ export class CommonFindController extends Disposable implements IEditorContribut
}
}
public start(opts: IFindStartOptions): void {
this._start(opts);
public start(opts: IFindStartOptions): Promise<void> {
return this._start(opts);
}
public moveToNextMatch(): boolean {
@@ -353,24 +353,24 @@ export class CommonFindController extends Disposable implements IEditorContribut
return false;
}
public getGlobalBufferTerm(): string {
public async getGlobalBufferTerm(): Promise<string> {
if (this._editor.getOption(EditorOption.find).globalFindClipboard
&& this._clipboardService
&& this._editor.hasModel()
&& !this._editor.getModel().isTooLargeForSyncing()
) {
return this._clipboardService.readFindTextSync();
return this._clipboardService.readFindText();
}
return '';
}
public setGlobalBufferTerm(text: string) {
public async setGlobalBufferTerm(text: string) {
if (this._editor.getOption(EditorOption.find).globalFindClipboard
&& this._clipboardService
&& this._editor.hasModel()
&& !this._editor.getModel().isTooLargeForSyncing()
) {
this._clipboardService.writeFindTextSync(text);
await this._clipboardService.writeFindText(text);
}
}
}
@@ -396,7 +396,7 @@ export class FindController extends CommonFindController implements IFindControl
this._findOptionsWidget = null;
}
protected _start(opts: IFindStartOptions): void {
protected async _start(opts: IFindStartOptions): Promise<void> {
if (!this._widget) {
this._createFindWidget();
}
@@ -422,7 +422,7 @@ export class FindController extends CommonFindController implements IFindControl
opts.updateSearchScope = updateSearchScope;
super._start(opts);
await super._start(opts);
if (opts.shouldFocus === FindStartFocusAction.FocusReplaceInput) {
this._widget!.focusReplaceInput();
@@ -505,7 +505,7 @@ export class StartFindWithSelectionAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
let controller = CommonFindController.get(editor);
if (controller) {
controller.start({
@@ -518,7 +518,7 @@ export class StartFindWithSelectionAction extends EditorAction {
loop: editor.getOption(EditorOption.find).loop
});
controller.setGlobalBufferTerm(controller.getState().searchString);
return controller.setGlobalBufferTerm(controller.getState().searchString);
}
}
}
+3 -3
View File
@@ -52,7 +52,7 @@ export const findNextMatchIcon = registerIcon('find-next-match', Codicon.arrowDo
export interface IFindController {
replace(): void;
replaceAll(): void;
getGlobalBufferTerm(): string;
getGlobalBufferTerm(): Promise<string>;
}
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
@@ -224,9 +224,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
this._updateToggleSelectionFindButton();
}
}));
this._register(this._codeEditor.onDidFocusEditorWidget(() => {
this._register(this._codeEditor.onDidFocusEditorWidget(async () => {
if (this._isVisible) {
let globalBufferTerm = this._controller.getGlobalBufferTerm();
let globalBufferTerm = await this._controller.getGlobalBufferTerm();
if (globalBufferTerm && globalBufferTerm !== this._state.searchString) {
this._state.change({ searchString: globalBufferTerm }, true);
this._findInput.select();
@@ -39,8 +39,8 @@ export class TestFindController extends CommonFindController {
this.hasFocus = false;
}
protected _start(opts: IFindStartOptions): void {
super._start(opts);
protected async _start(opts: IFindStartOptions): Promise<void> {
await super._start(opts);
if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) {
this.hasFocus = true;
@@ -31,14 +31,14 @@ var RECORDED_EVENTS = [];
var input = document.getElementById('input');
var blackListedProps = [
var blockedProperties = [
'currentTarget',
'path',
'srcElement',
'target',
'view'
];
blackListedProps = blackListedProps.concat([
blockedProperties = blockedProperties.concat([
'AT_TARGET',
'BLUR',
'BUBBLING_PHASE',
@@ -68,7 +68,7 @@ blackListedProps = blackListedProps.concat([
function toSerializable(e) {
var r = {};
for (var k in e) {
if (blackListedProps.indexOf(k) >= 0) {
if (blockedProperties.indexOf(k) >= 0) {
continue;
}
if (typeof e[k] === 'function') {
@@ -112,4 +112,4 @@ document.getElementById('stop').onclick = function() {
</script>
</body>
</html>
</html>
@@ -97,14 +97,4 @@ export class BrowserClipboardService implements IClipboardService {
async hasResources(): Promise<boolean> {
return this.resources.length > 0;
}
/** @deprecated */
readFindTextSync(): string {
return this.findText;
}
/** @deprecated */
writeFindTextSync(text: string): void {
this.findText = text;
}
}
@@ -46,12 +46,4 @@ export interface IClipboardService {
* Find out if resources are copied to the clipboard.
*/
hasResources(): Promise<boolean>;
/** @deprecated */
readFindTextSync(): string;
/** @deprecated */
writeFindTextSync(text: string): void;
}
+9 -3
View File
@@ -46,13 +46,19 @@ export async function readFromStdin(targetPath: string, verbose: boolean): Promi
let encoding = await resolveTerminalEncoding(verbose);
const iconv = await import('iconv-lite');
const iconv = await import('iconv-lite-umd');
if (!iconv.encodingExists(encoding)) {
console.log(`Unsupported terminal encoding: ${encoding}, falling back to UTF-8.`);
encoding = 'utf8';
}
// Pipe into tmp file using terminals encoding
const converterStream = iconv.decodeStream(encoding);
process.stdin.pipe(converterStream).pipe(stdinFileStream);
const decoder = iconv.getDecoder(encoding);
process.stdin.on('data', chunk => stdinFileStream.write(decoder.write(chunk)));
process.stdin.on('end', () => {
stdinFileStream.write(decoder.end());
stdinFileStream.end();
});
process.stdin.on('error', error => stdinFileStream.destroy(error));
process.stdin.on('close', () => stdinFileStream.close());
}
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { isWeb } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScannerService {
declare readonly _serviceBrand: undefined;
private readonly builtinExtensions: IScannedExtension[] = [];
constructor(
) {
if (isWeb) {
// Find builtin extensions by checking for DOM
const builtinExtensionsElement = document.getElementById('vscode-workbench-builtin-extensions');
const builtinExtensionsElementAttribute = builtinExtensionsElement ? builtinExtensionsElement.getAttribute('data-settings') : undefined;
if (builtinExtensionsElementAttribute) {
try {
const builtinExtensions: IScannedExtension[] = JSON.parse(builtinExtensionsElementAttribute);
this.builtinExtensions = builtinExtensions.map(e => <IScannedExtension>{
location: URI.revive(e.location),
type: ExtensionType.System,
packageJSON: e.packageJSON,
packageNLSUrl: URI.revive(e.packageNLSUrl),
readmeUrl: URI.revive(e.readmeUrl),
changelogUrl: URI.revive(e.changelogUrl),
});
} catch (error) { /* ignore error*/ }
}
}
}
async scanBuiltinExtensions(): Promise<IScannedExtension[]> {
if (isWeb) {
return this.builtinExtensions;
}
throw new Error('not supported');
}
}
@@ -6,6 +6,7 @@
import * as strings from 'vs/base/common/strings';
import { ILocalization } from 'vs/platform/localizations/common/localizations';
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const MANIFEST_CACHE_FOLDER = 'CachedExtensions';
export const USER_MANIFEST_CACHE_FILE = 'user';
@@ -246,3 +247,18 @@ export interface IExtensionDescription extends IExtensionManifest {
export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
}
export interface IScannedExtension {
readonly location: URI;
readonly type: ExtensionType;
readonly packageJSON: IExtensionManifest
readonly packageNLSUrl?: URI;
readonly readmeUrl?: URI;
readonly changelogUrl?: URI;
}
export const IBuiltinExtensionsScannerService = createDecorator<IBuiltinExtensionsScannerService>('IBuiltinExtensionsScannerService');
export interface IBuiltinExtensionsScannerService {
readonly _serviceBrand: undefined;
scanBuiltinExtensions(): Promise<IScannedExtension[]>;
}
@@ -156,6 +156,8 @@ export class LaunchMainService implements ILaunchMainService {
else {
const lastActive = this.windowsMainService.getLastActiveWindow();
if (lastActive) {
// Force focus the app before requesting window focus
app.focus({ steal: true });
lastActive.focus();
usedWindows = [lastActive];
+3 -3
View File
@@ -75,14 +75,14 @@ export class LoggerChannelClient {
export class FollowerLogService extends DelegatedLogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private master: LoggerChannelClient, logService: ILogService) {
constructor(private parent: LoggerChannelClient, logService: ILogService) {
super(logService);
this._register(master.onDidChangeLogLevel(level => logService.setLevel(level)));
this._register(parent.onDidChangeLogLevel(level => logService.setLevel(level)));
}
setLevel(level: LogLevel): void {
super.setLevel(level);
this.master.setLevel(level);
this.parent.setLevel(level);
}
}
@@ -63,17 +63,11 @@ export class BrowserStorageService extends Disposable implements IStorageService
// Workspace Storage
this.workspaceStorageFile = joinPath(stateRoot, `${payload.id}.json`);
this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService));
this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase));
this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE })));
const firstOpen = this.workspaceStorage.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN);
if (firstOpen === undefined) {
this.workspaceStorage.set(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, !(await this.fileService.exists(this.workspaceStorageFile)));
} else if (firstOpen) {
this.workspaceStorage.set(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, false);
}
// Global Storage
this.globalStorageFile = joinPath(stateRoot, 'global.json');
this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService));
@@ -86,6 +80,15 @@ export class BrowserStorageService extends Disposable implements IStorageService
this.globalStorage.init()
]);
// Check to see if this is the first time we are "opening" this workspace
const firstOpen = this.workspaceStorage.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN);
if (firstOpen === undefined) {
// NOTE@eamodio We can't reliably check to see if a workspace was added before this setting was introduced, so just pretend it is the first time
this.workspaceStorage.set(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, true);
} else if (firstOpen) {
this.workspaceStorage.set(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, false);
}
// In the browser we do not have support for long running unload sequences. As such,
// we cannot ask for saving state in that moment, because that would result in a
// long running operation.
@@ -105,6 +105,7 @@ export class NativeStorageService extends Disposable implements IStorageService
);
await workspaceStorage.init();
// Check to see if this is the first time we are "opening" this workspace
const firstOpen = workspaceStorage.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN);
if (firstOpen === undefined) {
workspaceStorage.set(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, !result.wasCreated);
@@ -81,6 +81,12 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
private readonly _onError: Emitter<UserDataSyncError> = this._register(new Emitter<UserDataSyncError>());
readonly onError: Event<UserDataSyncError> = this._onError.event;
private readonly _onTurnOnSync: Emitter<void> = this._register(new Emitter<void>());
readonly onTurnOnSync: Event<void> = this._onTurnOnSync.event;
private readonly _onDidTurnOnSync: Emitter<UserDataSyncError | undefined> = this._register(new Emitter<UserDataSyncError | undefined>());
readonly onDidTurnOnSync: Event<UserDataSyncError | undefined> = this._onDidTurnOnSync.event;
constructor(
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
@@ -140,15 +146,23 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
}
async turnOn(pullFirst: boolean): Promise<void> {
this.stopDisableMachineEventually();
this._onTurnOnSync.fire();
if (pullFirst) {
await this.userDataSyncService.pull();
} else {
await this.userDataSyncService.sync();
try {
this.stopDisableMachineEventually();
if (pullFirst) {
await this.userDataSyncService.pull();
} else {
await this.userDataSyncService.sync();
}
this.setEnablement(true);
this._onDidTurnOnSync.fire(undefined);
} catch (error) {
this._onDidTurnOnSync.fire(error);
throw error;
}
this.setEnablement(true);
}
async turnOff(everywhere: boolean, softTurnOffOnError?: boolean, donotRemoveMachine?: boolean): Promise<void> {
@@ -357,6 +357,7 @@ export interface IUserDataSyncService {
readonly status: SyncStatus;
readonly onDidChangeStatus: Event<SyncStatus>;
readonly onSynchronizeResource: Event<SyncResource>;
readonly conflicts: SyncResourceConflicts[];
readonly onDidChangeConflicts: Event<SyncResourceConflicts[]>;
@@ -390,6 +391,8 @@ export interface IUserDataSyncService {
export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService>('IUserDataAutoSyncService');
export interface IUserDataAutoSyncService {
_serviceBrand: any;
readonly onTurnOnSync: Event<void>
readonly onDidTurnOnSync: Event<UserDataSyncError | undefined>
readonly onError: Event<UserDataSyncError>;
readonly onDidChangeEnablement: Event<boolean>;
isEnabled(): boolean;
@@ -22,6 +22,7 @@ export class UserDataSyncChannel implements IServerChannel {
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
case 'onSynchronizeResource': return this.service.onSynchronizeResource;
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
case 'onDidChangeLastSyncTime': return this.service.onDidChangeLastSyncTime;
@@ -68,6 +69,8 @@ export class UserDataAutoSyncChannel implements IServerChannel {
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onTurnOnSync': return this.service.onTurnOnSync;
case 'onDidTurnOnSync': return this.service.onDidTurnOnSync;
case 'onError': return this.service.onError;
}
throw new Error(`Event not found: ${event}`);
@@ -39,6 +39,9 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
private _onSynchronizeResource: Emitter<SyncResource> = this._register(new Emitter<SyncResource>());
readonly onSynchronizeResource: Event<SyncResource> = this._onSynchronizeResource.event;
readonly onDidChangeLocal: Event<SyncResource>;
private _conflicts: SyncResourceConflicts[] = [];
@@ -91,6 +94,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
for (const synchroniser of this.synchronisers) {
try {
this._onSynchronizeResource.fire(synchroniser.resource);
await synchroniser.pull();
} catch (e) {
this.handleSynchronizerError(e, synchroniser.resource);
@@ -175,6 +179,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return;
}
try {
this._onSynchronizeResource.fire(synchroniser.resource);
await synchroniser.sync(manifest, syncHeaders);
} catch (e) {
this.handleSynchronizerError(e, synchroniser.resource);
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
import { VSBufferReadableStream } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { isUNC } from 'vs/base/common/extpath';
import { Schemas } from 'vs/base/common/network';
@@ -11,14 +11,12 @@ import { sep } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IRequestService } from 'vs/platform/request/common/request';
import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes';
export const webviewPartitionId = 'webview';
export namespace WebviewResourceResponse {
export enum Type { Success, Failed, AccessDenied }
@@ -31,73 +29,41 @@ export namespace WebviewResourceResponse {
) { }
}
export class BufferSuccess {
readonly type = Type.Success;
constructor(
public readonly buffer: VSBuffer,
public readonly mimeType: string
) { }
}
export const Failed = { type: Type.Failed } as const;
export const AccessDenied = { type: Type.AccessDenied } as const;
export type BufferResponse = BufferSuccess | typeof Failed | typeof AccessDenied;
export type StreamResponse = StreamSuccess | typeof Failed | typeof AccessDenied;
}
export async function loadLocalResource(
requestUri: URI,
fileService: IFileService,
extensionLocation: URI | undefined,
roots: ReadonlyArray<URI>
): Promise<WebviewResourceResponse.BufferResponse> {
const resourceToLoad = getResourceToLoad(requestUri, extensionLocation, roots);
if (!resourceToLoad) {
return WebviewResourceResponse.AccessDenied;
}
try {
const data = await fileService.readFile(resourceToLoad);
const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
return new WebviewResourceResponse.BufferSuccess(data.value, mime);
} catch (err) {
console.log(err);
return WebviewResourceResponse.Failed;
}
}
export async function loadLocalResourceStream(
requestUri: URI,
options: {
extensionLocation: URI | undefined;
roots: ReadonlyArray<URI>;
remoteConnectionData?: IRemoteConnectionData | null;
rewriteUri?: (uri: URI) => URI,
},
fileService: IFileService,
requestService: IRequestService,
): Promise<WebviewResourceResponse.StreamResponse> {
const resourceToLoad = getResourceToLoad(requestUri, options.extensionLocation, options.roots);
let resourceToLoad = getResourceToLoad(requestUri, options.roots);
if (!resourceToLoad) {
return WebviewResourceResponse.AccessDenied;
}
const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
if (options.remoteConnectionData) {
// Remote uris must go to the resolved server.
if (resourceToLoad.scheme === Schemas.vscodeRemote || (options.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
const uri = URI.parse(`http://${options.remoteConnectionData.host}:${options.remoteConnectionData.port}`).with({
path: '/vscode-remote-resource',
query: `tkn=${options.remoteConnectionData.connectionToken}&path=${encodeURIComponent(resourceToLoad.path)}`,
});
// Perform extra normalization if needed
if (options.rewriteUri) {
resourceToLoad = options.rewriteUri(resourceToLoad);
}
const response = await requestService.request({ url: uri.toString(true) }, CancellationToken.None);
if (response.res.statusCode === 200) {
return new WebviewResourceResponse.StreamSuccess(response.stream, mime);
}
return WebviewResourceResponse.Failed;
if (resourceToLoad.scheme === Schemas.http || resourceToLoad.scheme === Schemas.https) {
const response = await requestService.request({ url: resourceToLoad.toString(true) }, CancellationToken.None);
if (response.res.statusCode === 200) {
return new WebviewResourceResponse.StreamSuccess(response.stream, mime);
}
return WebviewResourceResponse.Failed;
}
try {
@@ -111,7 +77,6 @@ export async function loadLocalResourceStream(
function getResourceToLoad(
requestUri: URI,
extensionLocation: URI | undefined,
roots: ReadonlyArray<URI>
): URI | undefined {
const normalizedPath = normalizeRequestPath(requestUri);
@@ -12,7 +12,8 @@ import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRequestService } from 'vs/platform/request/common/request';
import { loadLocalResourceStream, webviewPartitionId, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
import { loadLocalResource, webviewPartitionId, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
interface WebviewMetadata {
readonly extensionLocation: URI | undefined;
@@ -39,11 +40,31 @@ export class WebviewProtocolProvider extends Disposable {
const id = uri.authority;
const metadata = this.webviewMetadata.get(id);
if (metadata) {
const result = await loadLocalResourceStream(uri, {
// Try to further rewrite remote uris so that they go to the resolved server on the main thread
let rewriteUri: undefined | ((uri: URI) => URI);
if (metadata.remoteConnectionData) {
rewriteUri = (uri) => {
if (metadata.remoteConnectionData) {
if (uri.scheme === Schemas.vscodeRemote || (metadata.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
const scheme = metadata.remoteConnectionData.host === 'localhost' || metadata.remoteConnectionData.host === '127.0.0.1' ? 'http' : 'https';
return URI.parse(`${scheme}://${metadata.remoteConnectionData.host}:${metadata.remoteConnectionData.port}`).with({
path: '/vscode-remote-resource',
query: `tkn=${metadata.remoteConnectionData.connectionToken}&path=${encodeURIComponent(uri.path)}`,
});
}
}
return uri;
};
}
const result = await loadLocalResource(uri, {
extensionLocation: metadata.extensionLocation,
roots: metadata.localResourceRoots,
remoteConnectionData: metadata.remoteConnectionData,
rewriteUri,
}, this.fileService, this.requestService);
if (result.type === WebviewResourceResponse.Type.Success) {
return callback({
statusCode: 200,
@@ -252,13 +252,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
// Note that onBeforeShutdown() and onBeforeWindowClose() are fired in different order depending on the OS:
// - macOS: since the app will not quit when closing the last window, you will always first get
// the onBeforeShutdown() event followed by N onbeforeWindowClose() events for each window
// the onBeforeShutdown() event followed by N onBeforeWindowClose() events for each window
// - other: on other OS, closing the last window will quit the app so the order depends on the
// user interaction: closing the last window will first trigger onBeforeWindowClose()
// and then onBeforeShutdown(). Using the quit action however will first issue onBeforeShutdown()
// and then onBeforeWindowClose().
//
// Here is the behaviour on different OS dependig on action taken (Electron 1.7.x):
// Here is the behavior on different OS depending on action taken (Electron 1.7.x):
//
// Legend
// - quit(N): quit application with N windows opened
@@ -320,7 +320,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
// 3.) All windows (except extension host) for N >= 2 to support restoreWindows: all or for auto update
//
// Carefull here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0)
// Careful here: asking a window for its window state after it has been closed returns bogus values (width: 0, height: 0)
// so if we ever want to persist the UI state of the last closed window (window count === 1), it has
// to come from the stored lastClosedWindowState on Win/Linux at least
if (this.getWindowCount() > 1) {
+58
View File
@@ -1054,6 +1054,64 @@ declare module 'vscode' {
//#endregion
//#region Terminal link provider https://github.com/microsoft/vscode/issues/91606
export namespace window {
export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable;
}
export interface TerminalLinkContext {
/**
* This is the text from the unwrapped line in the terminal.
*/
line: string;
/**
* The terminal the link belongs to.
*/
terminal: Terminal;
}
export interface TerminalLinkProvider<T = TerminalLink> {
provideTerminalLinks(context: TerminalLinkContext): ProviderResult<T[]>
/**
* Handle an activated terminal link.
*
* @returns Whether the link was handled, if not VS Code will attempt to open it.
*/
handleTerminalLink(link: T): ProviderResult<boolean>;
}
export interface TerminalLink {
/**
* The start index of the link on [TerminalLinkContext.line](#TerminalLinkContext.line].
*/
startIndex: number;
/**
* The length of the link on [TerminalLinkContext.line](#TerminalLinkContext.line]
*/
length: number;
/**
* The uri this link points to. If set, and {@link TerminalLinkProvider.handlerTerminalLink}
* is not implemented or returns false, then VS Code will try to open the Uri.
*/
target?: Uri;
/**
* The tooltip text when you hover over this link.
*
* If a tooltip is provided, is will be displayed in a string that includes instructions on
* how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary
* depending on OS, user settings, and localization.
*/
tooltip?: string;
}
//#endregion
//#region @jrieken -> exclusive document filters
export interface DocumentFilter {
@@ -24,9 +24,9 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
@@ -9,7 +9,7 @@ import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceS
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { StopWatch } from 'vs/base/common/stopwatch';
import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal';
import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
@@ -25,6 +25,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private readonly _terminalProcessProxies = new Map<number, ITerminalProcessExtHostProxy>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
private _linkHandler: IDisposable | undefined;
/**
* A single shared terminal link provider for the exthost. When an ext registers a link
* provider, this is registered with the terminal on the renderer side and all links are
* provided through this, even from multiple ext link providers. Xterm should remove lower
* priority intersecting links itself.
*/
private _linkProvider: IDisposable | undefined;
constructor(
extHostContext: IExtHostContext,
@@ -86,6 +93,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
public dispose(): void {
this._toDispose.dispose();
this._linkHandler?.dispose();
this._linkProvider?.dispose();
// TODO@Daniel: Should all the previously created terminals be disposed
// when the extension host process goes down ?
@@ -162,6 +171,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
public $stopHandlingLinks(): void {
this._linkHandler?.dispose();
this._linkHandler = undefined;
}
public $startLinkProvider(): void {
this._linkProvider?.dispose();
this._linkProvider = this._terminalService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy));
}
public $stopLinkProvider(): void {
this._linkProvider?.dispose();
this._linkProvider = undefined;
}
private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise<boolean> {
@@ -395,3 +415,22 @@ class TerminalDataEventTracker extends Disposable {
this._register(this._bufferer.startBuffering(instance.id, instance.onData));
}
}
class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider {
constructor(
private readonly _proxy: ExtHostTerminalServiceShape
) {
}
async provideLinks(instance: ITerminalInstance, line: string): Promise<ITerminalLink[] | undefined> {
const proxy = this._proxy;
const extHostLinks = await proxy.$provideLinks(instance.id, line);
return extHostLinks.map(dto => ({
id: dto.id,
startIndex: dto.startIndex,
length: dto.length,
label: dto.label,
activate: () => proxy.$activateLink(instance.id, dto.id)
}));
}
}
@@ -141,7 +141,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._register(_editorService.onDidActiveEditorChange(() => {
const activeInput = this._editorService.activeEditor;
if (activeInput instanceof DiffEditorInput && activeInput.master instanceof WebviewInput && activeInput.details instanceof WebviewInput) {
if (activeInput instanceof DiffEditorInput && activeInput.primary instanceof WebviewInput && activeInput.secondary instanceof WebviewInput) {
this.registerWebviewFromDiffEditorListeners(activeInput);
}
@@ -468,22 +468,22 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
}
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
const master = diffEditorInput.master as WebviewInput;
const details = diffEditorInput.details as WebviewInput;
const primary = diffEditorInput.primary as WebviewInput;
const secondary = diffEditorInput.secondary as WebviewInput;
if (this._webviewFromDiffEditorHandles.has(master.id) || this._webviewFromDiffEditorHandles.has(details.id)) {
if (this._webviewFromDiffEditorHandles.has(primary.id) || this._webviewFromDiffEditorHandles.has(secondary.id)) {
return;
}
this._webviewFromDiffEditorHandles.add(master.id);
this._webviewFromDiffEditorHandles.add(details.id);
this._webviewFromDiffEditorHandles.add(primary.id);
this._webviewFromDiffEditorHandles.add(secondary.id);
const disposables = new DisposableStore();
disposables.add(master.webview.onDidFocus(() => this.updateWebviewViewStates(master)));
disposables.add(details.webview.onDidFocus(() => this.updateWebviewViewStates(details)));
disposables.add(primary.webview.onDidFocus(() => this.updateWebviewViewStates(primary)));
disposables.add(secondary.webview.onDidFocus(() => this.updateWebviewViewStates(secondary)));
disposables.add(diffEditorInput.onDispose(() => {
this._webviewFromDiffEditorHandles.delete(master.id);
this._webviewFromDiffEditorHandles.delete(details.id);
this._webviewFromDiffEditorHandles.delete(primary.id);
this._webviewFromDiffEditorHandles.delete(secondary.id);
dispose(disposables);
}));
}
@@ -515,8 +515,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
for (const group of this._editorGroupService.groups) {
for (const input of group.editors) {
if (input instanceof DiffEditorInput) {
updateViewStatesForInput(group, input, input.master);
updateViewStatesForInput(group, input, input.details);
updateViewStatesForInput(group, input, input.primary);
updateViewStatesForInput(group, input, input.secondary);
} else {
updateViewStatesForInput(group, input, input);
}
@@ -46,7 +46,7 @@ import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { throwProposedApiError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
@@ -97,6 +97,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostLogService = accessor.get(ILogService);
const extHostTunnelService = accessor.get(IExtHostTunnelService);
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
const extHostWindow = accessor.get(IExtHostWindow);
// register addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, <ExtHostLogServiceShape><any>extHostLogService);
@@ -105,6 +106,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
// automatically create and register addressable instances
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
@@ -131,7 +133,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extensionStoragePaths));
@@ -586,6 +587,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTerminalService.registerLinkHandler(handler);
},
registerTerminalLinkProvider(handler: vscode.TerminalLinkProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostTerminalService.registerLinkProvider(handler);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
},
@@ -450,6 +450,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$stopSendingDataEvents(): void;
$startHandlingLinks(): void;
$stopHandlingLinks(): void;
$startLinkProvider(): void;
$stopLinkProvider(): void;
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void;
// Process
@@ -1380,6 +1382,17 @@ export interface IShellAndArgsDto {
args: string[] | string | undefined;
}
export interface ITerminalLinkDto {
/** The ID of the link to enable activation and disposal. */
id: number;
/** The startIndex of the link in the line. */
startIndex: number;
/** The length of the link in the line. */
length: number;
/** The descriptive label for what the link does when activated. */
label?: string;
}
export interface ITerminalDimensionsDto {
columns: number;
rows: number;
@@ -1406,6 +1419,8 @@ export interface ExtHostTerminalServiceShape {
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
$handleLink(id: number, link: string): Promise<boolean>;
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
$activateLink(id: number, linkId: number): void;
$initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
}
@@ -5,7 +5,7 @@
import type * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
@@ -39,6 +39,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable;
registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable;
getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
}
@@ -293,6 +294,13 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
}
}
let nextLinkId = 1;
interface ICachedLinkEntry {
provider: vscode.TerminalLinkProvider;
link: vscode.TerminalLink;
}
export abstract class BaseExtHostTerminalService implements IExtHostTerminalService, ExtHostTerminalServiceShape {
readonly _serviceBrand: undefined;
@@ -307,6 +315,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
private readonly _bufferer: TerminalDataBufferer;
private readonly _linkHandlers: Set<vscode.TerminalLinkHandler> = new Set();
private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
@@ -547,17 +557,30 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
public registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable {
this._linkHandlers.add(handler);
if (this._linkHandlers.size === 1) {
if (this._linkHandlers.size === 1 && this._linkProviders.size === 0) {
this._proxy.$startHandlingLinks();
}
return new VSCodeDisposable(() => {
this._linkHandlers.delete(handler);
if (this._linkHandlers.size === 0) {
if (this._linkHandlers.size === 0 && this._linkProviders.size === 0) {
this._proxy.$stopHandlingLinks();
}
});
}
public registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable {
this._linkProviders.add(provider);
if (this._linkProviders.size === 1) {
this._proxy.$startLinkProvider();
}
return new VSCodeDisposable(() => {
this._linkProviders.delete(provider);
if (this._linkProviders.size === 0) {
this._proxy.$stopLinkProvider();
}
});
}
public async $handleLink(id: number, link: string): Promise<boolean> {
const terminal = this._getTerminalById(id);
if (!terminal) {
@@ -577,6 +600,62 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
return false;
}
public async $provideLinks(terminalId: number, line: string): Promise<ITerminalLinkDto[]> {
const terminal = this._getTerminalById(terminalId);
if (!terminal) {
return [];
}
// Discard any cached links the terminal has been holding, currently all links are released
// when new links are provided.
this._terminalLinkCache.delete(terminalId);
const result: ITerminalLinkDto[] = [];
const context: vscode.TerminalLinkContext = { terminal, line };
const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = [];
for (const provider of this._linkProviders) {
promises.push(new Promise(async r => {
const links = (await provider.provideTerminalLinks(context)) || [];
r({ provider, links });
}));
}
const provideResults = await Promise.all(promises);
const cacheLinkMap = new Map<number, ICachedLinkEntry>();
for (const provideResult of provideResults) {
if (provideResult && provideResult.links.length > 0) {
result.push(...provideResult.links.map(providerLink => {
const link = {
id: nextLinkId++,
startIndex: providerLink.startIndex,
length: providerLink.length,
label: providerLink.tooltip
};
cacheLinkMap.set(link.id, {
provider: provideResult.provider,
link: providerLink
});
return link;
}));
}
}
this._terminalLinkCache.set(terminalId, cacheLinkMap);
return result;
}
$activateLink(terminalId: number, linkId: number): void {
const cachedLink = this._terminalLinkCache.get(terminalId)?.get(linkId);
if (!cachedLink) {
return;
}
cachedLink.provider.handleTerminalLink(cachedLink.link);
// TODO: Handle when result is false? Should this be return void instead and remove
// TerminalLink.target? It's a simple call to window.openUri for the extension otherwise
// and would simplify the API.
}
private _onProcessExit(id: number, exitCode: number | undefined): void {
this._bufferer.stopBuffering(id);
+1 -1
View File
@@ -1252,7 +1252,7 @@ export class MarkdownString {
// escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
this.value += (this.supportThemeIcons ? escapeCodicons(value) : value)
.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')
.replace('\n', '\n\n');
.replace(/\n/, '\n\n');
return this;
}
+9 -4
View File
@@ -4,13 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext, IOpenUriOptions } from './extHost.protocol';
import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IOpenUriOptions } from './extHost.protocol';
import { WindowState } from 'vscode';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export class ExtHostWindow implements ExtHostWindowShape {
export class ExtHostWindow implements IExtHostWindow {
private static InitialState: WindowState = {
focused: true
@@ -24,8 +26,8 @@ export class ExtHostWindow implements ExtHostWindowShape {
private _state = ExtHostWindow.InitialState;
get state(): WindowState { return this._state; }
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWindow);
constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
this._proxy = extHostRpc.getProxy(MainContext.MainThreadWindow);
this._proxy.$getWindowVisibility().then(isFocused => this.$onDidChangeWindowFocus(isFocused));
}
@@ -67,3 +69,6 @@ export class ExtHostWindow implements ExtHostWindowShape {
return URI.from(result);
}
}
export const IExtHostWindow = createDecorator<IExtHostWindow>('IExtHostWindow');
export interface IExtHostWindow extends ExtHostWindow, ExtHostWindowShape { }
@@ -29,12 +29,14 @@ import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService';
import { IExtHostApiDeprecationService, ExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
// register singleton services
registerSingleton(ILogService, ExtHostLogService);
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
registerSingleton(IExtHostOutputService, ExtHostOutputService2);
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
registerSingleton(IExtHostWindow, ExtHostWindow);
registerSingleton(IExtHostDecorations, ExtHostDecorations);
registerSingleton(IExtHostConfiguration, ExtHostConfiguration);
registerSingleton(IExtHostCommands, ExtHostCommands);
@@ -121,6 +121,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
}
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
return new ExtHostVariableResolverService(folders, undefined, configurationService, process.env as env.IProcessEnvironment);
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment);
}
}
@@ -138,7 +138,7 @@ CommandsRegistry.registerCommand({
handler: async function (accessor: ServicesAccessor, prefix: unknown) {
const quickInputService = accessor.get(IQuickInputService);
quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined);
quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined, { preserveValue: typeof prefix === 'string' /* preserve as is if provided */ });
},
description: {
description: `Quick access`,
+2 -2
View File
@@ -5,7 +5,7 @@
import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData, IRecentFile, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { normalize } from 'vs/base/common/path';
import { basename } from 'vs/base/common/resources';
import { basename, extUri } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
@@ -357,7 +357,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
for (const textEditorControl of textEditorControls) {
if (isCodeEditor(textEditorControl)) {
const model = textEditorControl.getModel();
if (model?.uri?.toString() === file.resource.toString()) {
if (extUri.isEqual(model?.uri, file.resource)) {
return withNullAsUndefined(textEditorControl.saveViewState());
}
}
+7 -7
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources';
import { dirname, isEqual, basenameOrAuthority, extUri } from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IResourceLabelProps {
resource?: URI | { master?: URI, detail?: URI };
resource?: URI | { primary?: URI, secondary?: URI };
name?: string | string[];
description?: string;
}
@@ -38,7 +38,7 @@ function toResource(props: IResourceLabelProps | undefined): URI | undefined {
return props.resource;
}
return props.resource.master;
return props.resource.primary;
}
export interface IResourceLabelOptions extends IIconLabelValueOptions {
@@ -307,7 +307,7 @@ class ResourceLabelWidget extends IconLabel {
return; // only update if resource exists
}
if (model.uri.toString() === resource.toString()) {
if (extUri.isEqual(model.uri, resource)) {
if (this.lastKnownDetectedModeId !== model.getModeId()) {
this.render(true); // update if the language id of the model has changed from our last known state
}
@@ -379,9 +379,9 @@ class ResourceLabelWidget extends IconLabel {
setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void {
const resource = toResource(label);
const isMasterDetail = label?.resource && !URI.isUri(label.resource);
const isSideBySideEditor = label?.resource && !URI.isUri(label.resource);
if (!options.forceLabel && !isMasterDetail && resource?.scheme === Schemas.untitled) {
if (!options.forceLabel && !isSideBySideEditor && resource?.scheme === Schemas.untitled) {
// Untitled labels are very dynamic because they may change
// whenever the content changes (unless a path is associated).
// As such we always ask the actual editor for it's name and
@@ -390,7 +390,7 @@ class ResourceLabelWidget extends IconLabel {
// we assume that the client does not want to display them
// and as such do not override.
//
// We do not touch the label if it represents a master-detail
// We do not touch the label if it represents a primary-secondary
// because in that case we expect it to carry a proper label
// and description.
const untitledModel = this.textFileService.untitled.get(resource);
+4 -2
View File
@@ -568,7 +568,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
return;
}
const firstOpen = storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE);
// The `firstRun` flag check is a safety-net hack for Codespaces, until we can verify the first open fix
const firstOpen = (storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE) || (defaultLayout as { firstRun: boolean })?.firstRun);
if (!firstOpen) {
return;
}
@@ -790,7 +791,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
private getInitialFilesToOpen(): { filesToOpenOrCreate?: IPath[], filesToDiff?: IPath[] } | undefined {
const defaultLayout = this.environmentService.options?.defaultLayout;
if (defaultLayout?.editors?.length && this.storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE)) {
// The `firstRun` flag check is a safety-net hack for Codespaces, until we can verify the first open fix
if (defaultLayout?.editors?.length && (this.storageService.getBoolean(WorkspaceStorageSettings.WORKSPACE_FIRST_OPEN, StorageScope.WORKSPACE) || (defaultLayout as { firstRun: boolean })?.firstRun)) {
this._openedDefaultEditors = true;
return {
@@ -29,11 +29,11 @@ export class BinaryResourceDiffEditor extends SideBySideEditor {
}
getMetadata(): string | undefined {
const master = this.masterEditorPane;
const details = this.detailsEditorPane;
const primary = this.primaryEditorPane;
const secondary = this.secondaryEditorPane;
if (master instanceof BaseBinaryResourceEditor && details instanceof BaseBinaryResourceEditor) {
return nls.localize('metadataDiff', "{0} ↔ {1}", details.getMetadata(), master.getMetadata());
if (primary instanceof BaseBinaryResourceEditor && secondary instanceof BaseBinaryResourceEditor) {
return nls.localize('metadataDiff', "{0} ↔ {1}", secondary.getMetadata(), primary.getMetadata());
}
return undefined;
@@ -234,7 +234,7 @@ export class BreadcrumbsControl {
this._breadcrumbsDisposables.clear();
// honor diff editors and such
const uri = toResource(this._editorGroup.activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
const uri = toResource(this._editorGroup.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!uri || !this._fileService.canHandleResource(uri)) {
// cleanup and return when there is no input or when
@@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as nls from 'vs/nls';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext } from 'vs/workbench/common/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext, EditorStickyContext, ActiveEditorAvailableEditorIdsContext } from 'vs/workbench/common/editor';
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
@@ -34,7 +34,7 @@ import {
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction,
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReopenResourcesAction, ToggleEditorTypeAction
} from 'vs/workbench/browser/parts/editor/editorActions';
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -173,28 +173,28 @@ interface ISerializedSideBySideEditorInput {
name: string;
description: string | undefined;
detailsSerialized: string;
masterSerialized: string;
primarySerialized: string;
secondarySerialized: string;
detailsTypeId: string;
masterTypeId: string;
primaryTypeId: string;
secondaryTypeId: string;
}
export abstract class AbstractSideBySideEditorInputFactory implements IEditorInputFactory {
private getInputFactories(detailsId: string, masterId: string): [IEditorInputFactory | undefined, IEditorInputFactory | undefined] {
private getInputFactories(secondaryId: string, primaryId: string): [IEditorInputFactory | undefined, IEditorInputFactory | undefined] {
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
return [registry.getEditorInputFactory(detailsId), registry.getEditorInputFactory(masterId)];
return [registry.getEditorInputFactory(secondaryId), registry.getEditorInputFactory(primaryId)];
}
canSerialize(editorInput: EditorInput): boolean {
const input = editorInput as SideBySideEditorInput | DiffEditorInput;
if (input.details && input.master) {
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(input.details.getTypeId(), input.master.getTypeId());
if (input.primary && input.secondary) {
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId());
return !!(detailsInputFactory?.canSerialize(input.details) && masterInputFactory?.canSerialize(input.master));
return !!(secondaryInputFactory?.canSerialize(input.secondary) && primaryInputFactory?.canSerialize(input.primary));
}
return false;
@@ -203,20 +203,20 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
serialize(editorInput: EditorInput): string | undefined {
const input = editorInput as SideBySideEditorInput | DiffEditorInput;
if (input.details && input.master) {
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(input.details.getTypeId(), input.master.getTypeId());
if (detailsInputFactory && masterInputFactory) {
const detailsSerialized = detailsInputFactory.serialize(input.details);
const masterSerialized = masterInputFactory.serialize(input.master);
if (input.primary && input.secondary) {
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(input.secondary.getTypeId(), input.primary.getTypeId());
if (primaryInputFactory && secondaryInputFactory) {
const primarySerialized = primaryInputFactory.serialize(input.primary);
const secondarySerialized = secondaryInputFactory.serialize(input.secondary);
if (detailsSerialized && masterSerialized) {
if (primarySerialized && secondarySerialized) {
const serializedEditorInput: ISerializedSideBySideEditorInput = {
name: input.getName(),
description: input.getDescription(),
detailsSerialized,
masterSerialized,
detailsTypeId: input.details.getTypeId(),
masterTypeId: input.master.getTypeId()
primarySerialized: primarySerialized,
secondarySerialized: secondarySerialized,
primaryTypeId: input.primary.getTypeId(),
secondaryTypeId: input.secondary.getTypeId()
};
return JSON.stringify(serializedEditorInput);
@@ -230,33 +230,33 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const [detailsInputFactory, masterInputFactory] = this.getInputFactories(deserialized.detailsTypeId, deserialized.masterTypeId);
if (detailsInputFactory && masterInputFactory) {
const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized);
const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized);
const [secondaryInputFactory, primaryInputFactory] = this.getInputFactories(deserialized.secondaryTypeId, deserialized.primaryTypeId);
if (primaryInputFactory && secondaryInputFactory) {
const primaryInput = primaryInputFactory.deserialize(instantiationService, deserialized.primarySerialized);
const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized);
if (detailsInput && masterInput) {
return this.createEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput);
if (primaryInput && secondaryInput) {
return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput);
}
}
return undefined;
}
protected abstract createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput;
protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
}
class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput {
return new SideBySideEditorInput(name, description, detailsInput, masterInput);
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new SideBySideEditorInput(name, description, secondaryInput, primaryInput);
}
}
class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, detailsInput: EditorInput, masterInput: EditorInput): EditorInput {
return new DiffEditorInput(name, description, detailsInput, masterInput);
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new DiffEditorInput(name, description, secondaryInput, primaryInput);
}
}
@@ -386,6 +386,8 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutThreeRows
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoByTwoGridAction), 'View: Grid Editor Layout (2x2)', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoRowsRightAction), 'View: Two Rows Right Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(EditorLayoutTwoColumnsBottomAction), 'View: Two Columns Bottom Editor Layout', category);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ReopenResourcesAction), 'View: Reopen Editor With...', category, ActiveEditorAvailableEditorIdsContext);
registry.registerWorkbenchAction(SyncActionDescriptor.from(ToggleEditorTypeAction), 'View: Toggle Editor Type', category, ActiveEditorAvailableEditorIdsContext);
// Register Quick Editor Actions including built in quick navigate support for some
@@ -461,6 +463,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: ReopenResourcesAction.ID, title: ReopenResourcesAction.LABEL }, group: '6_reopen', order: 20, when: ActiveEditorAvailableEditorIdsContext });
interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }
@@ -11,7 +11,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { DisposableStore } from 'vs/base/common/lifecycle';
@@ -22,6 +22,7 @@ import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/commo
import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
import { Codicon } from 'vs/base/common/codicons';
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { openEditorWith, getAllAvailableEditors } from 'vs/workbench/services/editor/common/editorOpenWith';
export class ExecuteCommandAction extends Action {
@@ -580,7 +581,7 @@ abstract class BaseCloseAllAction extends Action {
else {
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
name = editor.primary.getName(); // prefer shorter names by using primary's name in this case
} else {
name = editor.getName();
}
@@ -1775,3 +1776,72 @@ export class NewEditorGroupBelowAction extends BaseCreateEditorGroupAction {
super(id, label, GroupDirection.DOWN, editorGroupService);
}
}
export class ReopenResourcesAction extends Action {
static readonly ID = 'workbench.action.reopenWithEditor';
static readonly LABEL = nls.localize('workbench.action.reopenWithEditor', "Reopen Editor With...");
constructor(
id: string,
label: string,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IEditorService private readonly editorService: IEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(id, label);
}
async run(): Promise<void> {
const activeInput = this.editorService.activeEditor;
if (!activeInput) {
return;
}
const activeEditorPane = this.editorService.activeEditorPane;
if (!activeEditorPane) {
return;
}
const options = activeEditorPane.options;
const group = activeEditorPane.group;
await openEditorWith(activeInput, undefined, options, group, this.editorService, this.configurationService, this.quickInputService);
}
}
export class ToggleEditorTypeAction extends Action {
static readonly ID = 'workbench.action.toggleEditorType';
static readonly LABEL = nls.localize('workbench.action.toggleEditorType', "Toggle Editor Type");
constructor(
id: string,
label: string,
@IEditorService private readonly editorService: IEditorService,
) {
super(id, label);
}
async run(): Promise<void> {
const activeEditorPane = this.editorService.activeEditorPane;
if (!activeEditorPane) {
return;
}
const input = activeEditorPane.input;
if (!input.resource) {
return;
}
const options = activeEditorPane.options;
const group = activeEditorPane.group;
const overrides = getAllAvailableEditors(input.resource, options, group, this.editorService);
const firstNonActiveOverride = overrides.find(([_, entry]) => !entry.active);
if (!firstNonActiveOverride) {
return;
}
await firstNonActiveOverride[0].open(input, { ...options, override: firstNonActiveOverride[1].id }, group, OpenEditorContext.NEW_EDITOR)?.override;
}
}
@@ -530,8 +530,12 @@ class DropOverlay extends Themable {
}
}
export interface EditorDropTargetDelegate {
groupContainsPredicate?(groupView: IEditorGroupView): boolean;
export interface IEditorDropTargetDelegate {
/**
* A helper to figure out if the drop target contains the provided group.
*/
containsGroup?(groupView: IEditorGroupView): boolean;
}
export class EditorDropTarget extends Themable {
@@ -546,7 +550,7 @@ export class EditorDropTarget extends Themable {
constructor(
private accessor: IEditorGroupsAccessor,
private container: HTMLElement,
private readonly delegate: EditorDropTargetDelegate,
private readonly delegate: IEditorDropTargetDelegate,
@IThemeService themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
@@ -620,7 +624,8 @@ export class EditorDropTarget extends Themable {
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
const groups = this.accessor.groups;
return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.groupContainsPredicate?.(groupView));
return groups.find(groupView => isAncestor(child, groupView.element) || this.delegate.containsGroup?.(groupView));
}
private updateContainer(isDraggedOver: boolean): void {
@@ -527,7 +527,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Include both sides of side by side editors when being closed
if (editor instanceof SideBySideEditorInput) {
editorsToClose.push(editor.master, editor.details);
editorsToClose.push(editor.primary, editor.secondary);
}
// For each editor to close, we call dispose() to free up any resources.
@@ -537,7 +537,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
for (const editor of editorsToClose) {
if (!this.accessor.groups.some(groupView => groupView.group.contains(editor, {
strictEquals: true, // only if this input is not shared across editor groups
supportSideBySide: true // include side by side editor master & details
supportSideBySide: true // include side by side editor primary & secondary
}))) {
editor.dispose();
}
@@ -1359,8 +1359,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return false; // editor must be dirty and not saving
}
if (editor instanceof SideBySideEditorInput && this._group.contains(editor.master)) {
return false; // master-side of editor is still opened somewhere else
if (editor instanceof SideBySideEditorInput && this._group.contains(editor.primary)) {
return false; // primary-side of editor is still opened somewhere else
}
// Note: we explicitly decide to ask for confirm if closing a normal editor even
@@ -1378,8 +1378,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return true; // exact editor still opened
}
if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.master)) {
return true; // master side of side by side editor still opened
if (editor instanceof SideBySideEditorInput && otherGroup.contains(editor.primary)) {
return true; // primary side of side by side editor still opened
}
return false;
@@ -1404,7 +1404,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
let name: string;
if (editor instanceof SideBySideEditorInput) {
name = editor.master.getName(); // prefer shorter names by using master's name in this case
name = editor.primary.getName(); // prefer shorter names by using primary's name in this case
} else {
name = editor.getName();
}
@@ -21,7 +21,8 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
import { Color } from 'vs/base/common/color';
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -780,6 +781,14 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
//#endregion
//#region IEditorDropService
createEditorDropTarget(container: HTMLElement, delegate: IEditorDropTargetDelegate): IDisposable {
return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate);
}
//#endregion
//#region Part
// TODO @sbatten @joao find something better to prevent editor taking over #79897
@@ -820,7 +829,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
// Drop support
this._register(this.createEditorDropTarget(this.container, {}));
this._register(this.createEditorDropTarget(this.container, Object.create(null)));
// No drop in the editor
const overlay = document.createElement('div');
@@ -1097,16 +1106,24 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
}
// Persist centered view state
const centeredLayoutState = this.centeredLayoutWidget.state;
if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) {
delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY];
} else {
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState;
if (this.centeredLayoutWidget) {
const centeredLayoutState = this.centeredLayoutWidget.state;
if (this.centeredLayoutWidget.isDefault(centeredLayoutState)) {
delete this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY];
} else {
this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY] = centeredLayoutState;
}
}
super.saveState();
}
toJSON(): object {
return {
type: Parts.EDITOR_PART
};
}
dispose(): void {
// Forward to all groups
@@ -1122,20 +1139,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
}
//#endregion
toJSON(): object {
return {
type: Parts.EDITOR_PART
};
}
//#region TODO@matt this should move into some kind of service
createEditorDropTarget(container: HTMLElement, delegate: EditorDropTargetDelegate): IDisposable {
return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate);
}
//#endregion
}
registerSingleton(IEditorGroupsService, EditorPart);
registerSingleton(IEditorDropService, EditorPart);
@@ -137,7 +137,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
}
return this.doGetEditors().map(({ editor, groupId }): IEditorQuickPickItem => {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
const isDirty = editor.isDirty() && !editor.isSaving();
const description = editor.getDescription();
const nameAndDescription = description ? `${editor.getName()} ${description}` : editor.getName();
@@ -54,22 +54,22 @@ import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGRO
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
constructor(private primary: IEncodingSupport, private secondary: IEncodingSupport) { }
getEncoding(): string | undefined {
return this.master.getEncoding(); // always report from modified (right hand) side
return this.primary.getEncoding(); // always report from modified (right hand) side
}
setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(editor => editor.setEncoding(encoding, mode));
[this.primary, this.secondary].forEach(editor => editor.setEncoding(encoding, mode));
}
}
class SideBySideEditorModeSupport implements IModeSupport {
constructor(private master: IModeSupport, private details: IModeSupport) { }
constructor(private primary: IModeSupport, private secondary: IModeSupport) { }
setMode(mode: string): void {
[this.master, this.details].forEach(editor => editor.setMode(mode));
[this.primary, this.secondary].forEach(editor => editor.setMode(mode));
}
}
@@ -82,14 +82,14 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterEncodingSupport = toEditorWithEncodingSupport(input.master);
const detailsEncodingSupport = toEditorWithEncodingSupport(input.details);
const primaryEncodingSupport = toEditorWithEncodingSupport(input.primary);
const secondaryEncodingSupport = toEditorWithEncodingSupport(input.secondary);
if (masterEncodingSupport && detailsEncodingSupport) {
return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport);
if (primaryEncodingSupport && secondaryEncodingSupport) {
return new SideBySideEditorEncodingSupport(primaryEncodingSupport, secondaryEncodingSupport);
}
return masterEncodingSupport;
return primaryEncodingSupport;
}
// File or Resource Editor
@@ -111,14 +111,14 @@ function toEditorWithModeSupport(input: IEditorInput): IModeSupport | null {
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterModeSupport = toEditorWithModeSupport(input.master);
const detailsModeSupport = toEditorWithModeSupport(input.details);
const primaryModeSupport = toEditorWithModeSupport(input.primary);
const secondaryModeSupport = toEditorWithModeSupport(input.secondary);
if (masterModeSupport && detailsModeSupport) {
return new SideBySideEditorModeSupport(masterModeSupport, detailsModeSupport);
if (primaryModeSupport && secondaryModeSupport) {
return new SideBySideEditorModeSupport(primaryModeSupport, secondaryModeSupport);
}
return masterModeSupport;
return primaryModeSupport;
}
// File or Resource Editor
@@ -685,14 +685,14 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
else if (activeEditorPane instanceof BaseBinaryResourceEditor || activeEditorPane instanceof BinaryResourceDiffEditor) {
const binaryEditors: BaseBinaryResourceEditor[] = [];
if (activeEditorPane instanceof BinaryResourceDiffEditor) {
const details = activeEditorPane.getDetailsEditorPane();
if (details instanceof BaseBinaryResourceEditor) {
binaryEditors.push(details);
const primary = activeEditorPane.getPrimaryEditorPane();
if (primary instanceof BaseBinaryResourceEditor) {
binaryEditors.push(primary);
}
const master = activeEditorPane.getMasterEditorPane();
if (master instanceof BaseBinaryResourceEditor) {
binaryEditors.push(master);
const secondary = activeEditorPane.getSecondaryEditorPane();
if (secondary instanceof BaseBinaryResourceEditor) {
binaryEditors.push(secondary);
}
} else {
binaryEditors.push(activeEditorPane);
@@ -871,7 +871,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
private onResourceEncodingChange(resource: URI): void {
const activeEditorPane = this.editorService.activeEditorPane;
if (activeEditorPane) {
const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER });
const activeResource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY });
if (activeResource && isEqual(activeResource, resource)) {
const activeCodeEditor = withNullAsUndefined(getCodeEditor(activeEditorPane.getControl()));
@@ -1064,7 +1064,7 @@ export class ChangeModeAction extends Action {
}
const textModel = activeTextEditorControl.getModel();
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY }) : null;
let hasLanguageSupport = !!resource;
if (resource?.scheme === Schemas.untitled && !this.textFileService.untitled.get(resource)?.hasAssociatedFilePath) {
@@ -1161,7 +1161,7 @@ export class ChangeModeAction extends Action {
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
if (textModel) {
const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource, textModel.getLineContent(1));
}
@@ -1356,7 +1356,7 @@ export class ChangeEncodingAction extends Action {
await timeout(50); // quick input is sensitive to being opened so soon after another
const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(activeEditorPane.input, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!resource || (!this.fileService.canHandleResource(resource) && resource.scheme !== Schemas.untitled)) {
return; // encoding detection only possible for resources the file service can handle or that are untitled
}
@@ -185,7 +185,7 @@ export class EditorsObserver extends Disposable {
}
private updateEditorResourcesMap(editor: IEditorInput, add: boolean): void {
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (!resource) {
return; // require a resource
}
@@ -22,17 +22,16 @@ import { assertIsDefined } from 'vs/base/common/types';
export class SideBySideEditor extends BaseEditor {
static readonly ID: string = 'workbench.editor.sidebysideEditor';
static MASTER: SideBySideEditor | undefined;
get minimumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.minimumWidth : 0; }
get maximumMasterWidth() { return this.masterEditorPane ? this.masterEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.minimumHeight : 0; }
get maximumMasterHeight() { return this.masterEditorPane ? this.masterEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
private get minimumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.minimumWidth : 0; }
private get maximumPrimaryWidth() { return this.primaryEditorPane ? this.primaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
private get minimumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.minimumHeight : 0; }
private get maximumPrimaryHeight() { return this.primaryEditorPane ? this.primaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
get minimumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.minimumWidth : 0; }
get maximumDetailsWidth() { return this.detailsEditorPane ? this.detailsEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
get minimumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.minimumHeight : 0; }
get maximumDetailsHeight() { return this.detailsEditorPane ? this.detailsEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
private get minimumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumWidth : 0; }
private get maximumSecondaryWidth() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumWidth : Number.POSITIVE_INFINITY; }
private get minimumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.minimumHeight : 0; }
private get maximumSecondaryHeight() { return this.secondaryEditorPane ? this.secondaryEditorPane.maximumHeight : Number.POSITIVE_INFINITY; }
// these setters need to exist because this extends from BaseEditor
set minimumWidth(value: number) { /* noop */ }
@@ -40,16 +39,16 @@ export class SideBySideEditor extends BaseEditor {
set minimumHeight(value: number) { /* noop */ }
set maximumHeight(value: number) { /* noop */ }
get minimumWidth() { return this.minimumMasterWidth + this.minimumDetailsWidth; }
get maximumWidth() { return this.maximumMasterWidth + this.maximumDetailsWidth; }
get minimumHeight() { return this.minimumMasterHeight + this.minimumDetailsHeight; }
get maximumHeight() { return this.maximumMasterHeight + this.maximumDetailsHeight; }
get minimumWidth() { return this.minimumPrimaryWidth + this.minimumSecondaryWidth; }
get maximumWidth() { return this.maximumPrimaryWidth + this.maximumSecondaryWidth; }
get minimumHeight() { return this.minimumPrimaryHeight + this.minimumSecondaryHeight; }
get maximumHeight() { return this.maximumPrimaryHeight + this.maximumSecondaryHeight; }
protected masterEditorPane?: BaseEditor;
protected detailsEditorPane?: BaseEditor;
protected primaryEditorPane?: BaseEditor;
protected secondaryEditorPane?: BaseEditor;
private masterEditorContainer: HTMLElement | undefined;
private detailsEditorContainer: HTMLElement | undefined;
private primaryEditorContainer: HTMLElement | undefined;
private secondaryEditorContainer: HTMLElement | undefined;
private splitview: SplitView | undefined;
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
@@ -74,19 +73,19 @@ export class SideBySideEditor extends BaseEditor {
const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes()));
this.detailsEditorContainer = DOM.$('.details-editor-container');
this.secondaryEditorContainer = DOM.$('.secondary-editor-container');
this.splitview.addView({
element: this.detailsEditorContainer,
layout: size => this.detailsEditorPane && this.detailsEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
element: this.secondaryEditorContainer,
layout: size => this.secondaryEditorPane && this.secondaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this.masterEditorContainer = DOM.$('.master-editor-container');
this.primaryEditorContainer = DOM.$('.primary-editor-container');
this.splitview.addView({
element: this.masterEditorContainer,
layout: size => this.masterEditorPane && this.masterEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
element: this.primaryEditorContainer,
layout: size => this.primaryEditorPane && this.primaryEditorPane.layout(new DOM.Dimension(size, this.dimension.height)),
minimumSize: 220,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
@@ -103,30 +102,30 @@ export class SideBySideEditor extends BaseEditor {
}
setOptions(options: EditorOptions | undefined): void {
if (this.masterEditorPane) {
this.masterEditorPane.setOptions(options);
if (this.primaryEditorPane) {
this.primaryEditorPane.setOptions(options);
}
}
protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
if (this.masterEditorPane) {
this.masterEditorPane.setVisible(visible, group);
if (this.primaryEditorPane) {
this.primaryEditorPane.setVisible(visible, group);
}
if (this.detailsEditorPane) {
this.detailsEditorPane.setVisible(visible, group);
if (this.secondaryEditorPane) {
this.secondaryEditorPane.setVisible(visible, group);
}
super.setEditorVisible(visible, group);
}
clearInput(): void {
if (this.masterEditorPane) {
this.masterEditorPane.clearInput();
if (this.primaryEditorPane) {
this.primaryEditorPane.clearInput();
}
if (this.detailsEditorPane) {
this.detailsEditorPane.clearInput();
if (this.secondaryEditorPane) {
this.secondaryEditorPane.clearInput();
}
this.disposeEditors();
@@ -135,8 +134,8 @@ export class SideBySideEditor extends BaseEditor {
}
focus(): void {
if (this.masterEditorPane) {
this.masterEditorPane.focus();
if (this.primaryEditorPane) {
this.primaryEditorPane.focus();
}
}
@@ -148,19 +147,19 @@ export class SideBySideEditor extends BaseEditor {
}
getControl(): IEditorControl | undefined {
if (this.masterEditorPane) {
return this.masterEditorPane.getControl();
if (this.primaryEditorPane) {
return this.primaryEditorPane.getControl();
}
return undefined;
}
getMasterEditorPane(): IEditorPane | undefined {
return this.masterEditorPane;
getPrimaryEditorPane(): IEditorPane | undefined {
return this.primaryEditorPane;
}
getDetailsEditorPane(): IEditorPane | undefined {
return this.detailsEditorPane;
getSecondaryEditorPane(): IEditorPane | undefined {
return this.secondaryEditorPane;
}
private async updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
@@ -172,21 +171,21 @@ export class SideBySideEditor extends BaseEditor {
return this.setNewInput(newInput, options, token);
}
if (!this.detailsEditorPane || !this.masterEditorPane) {
if (!this.secondaryEditorPane || !this.primaryEditorPane) {
return;
}
await Promise.all([
this.detailsEditorPane.setInput(newInput.details, undefined, token),
this.masterEditorPane.setInput(newInput.master, options, token)
this.secondaryEditorPane.setInput(newInput.secondary, undefined, token),
this.primaryEditorPane.setInput(newInput.primary, options, token)
]);
}
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
const detailsEditor = this.doCreateEditor(newInput.details, assertIsDefined(this.detailsEditorContainer));
const masterEditor = this.doCreateEditor(newInput.master, assertIsDefined(this.masterEditorContainer));
const secondaryEditor = this.doCreateEditor(newInput.secondary, assertIsDefined(this.secondaryEditorContainer));
const primaryEditor = this.doCreateEditor(newInput.primary, assertIsDefined(this.primaryEditorContainer));
return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token);
return this.onEditorsCreated(secondaryEditor, primaryEditor, newInput.secondary, newInput.primary, options, token);
}
private doCreateEditor(editorInput: EditorInput, container: HTMLElement): BaseEditor {
@@ -202,48 +201,48 @@ export class SideBySideEditor extends BaseEditor {
return editor;
}
private async onEditorsCreated(details: BaseEditor, master: BaseEditor, detailsInput: EditorInput, masterInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
this.detailsEditorPane = details;
this.masterEditorPane = master;
private async onEditorsCreated(secondary: BaseEditor, primary: BaseEditor, secondaryInput: EditorInput, primaryInput: EditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
this.secondaryEditorPane = secondary;
this.primaryEditorPane = primary;
this._onDidSizeConstraintsChange.input = Event.any(
Event.map(details.onDidSizeConstraintsChange, () => undefined),
Event.map(master.onDidSizeConstraintsChange, () => undefined)
Event.map(secondary.onDidSizeConstraintsChange, () => undefined),
Event.map(primary.onDidSizeConstraintsChange, () => undefined)
);
this.onDidCreateEditors.fire(undefined);
await Promise.all([
this.detailsEditorPane.setInput(detailsInput, undefined, token),
this.masterEditorPane.setInput(masterInput, options, token)]
this.secondaryEditorPane.setInput(secondaryInput, undefined, token),
this.primaryEditorPane.setInput(primaryInput, options, token)]
);
}
updateStyles(): void {
super.updateStyles();
if (this.masterEditorContainer) {
this.masterEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`;
if (this.primaryEditorContainer) {
this.primaryEditorContainer.style.boxShadow = `-6px 0 5px -5px ${this.getColor(scrollbarShadow)}`;
}
}
private disposeEditors(): void {
if (this.detailsEditorPane) {
this.detailsEditorPane.dispose();
this.detailsEditorPane = undefined;
if (this.secondaryEditorPane) {
this.secondaryEditorPane.dispose();
this.secondaryEditorPane = undefined;
}
if (this.masterEditorPane) {
this.masterEditorPane.dispose();
this.masterEditorPane = undefined;
if (this.primaryEditorPane) {
this.primaryEditorPane.dispose();
this.primaryEditorPane = undefined;
}
if (this.detailsEditorContainer) {
DOM.clearNode(this.detailsEditorContainer);
if (this.secondaryEditorContainer) {
DOM.clearNode(this.secondaryEditorContainer);
}
if (this.masterEditorContainer) {
DOM.clearNode(this.masterEditorContainer);
if (this.primaryEditorContainer) {
DOM.clearNode(this.primaryEditorContainer);
}
}
@@ -1087,7 +1087,7 @@ export class TabsTitleControl extends TitleControl {
);
// Tests helper
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.PRIMARY });
if (resource) {
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
} else {

Some files were not shown because too many files have changed in this diff Show More