Merge from vscode 6fded8a497
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
typeLabel: {
|
||||
name: 'feature-request'
|
||||
},
|
||||
candidateMilestone: {
|
||||
number: 107,
|
||||
name: 'Backlog Candidates'
|
||||
},
|
||||
approvedMilestone: {
|
||||
number: 8,
|
||||
name: 'Backlog'
|
||||
},
|
||||
onLabeled: {
|
||||
delay: 60,
|
||||
perform: true
|
||||
},
|
||||
onCandidateMilestoned: {
|
||||
candidatesComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!",
|
||||
perform: true
|
||||
},
|
||||
onMonitorUpvotes: {
|
||||
upvoteThreshold: 20,
|
||||
acceptanceComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!",
|
||||
perform: true
|
||||
},
|
||||
onMonitorDaysOnCandidateMilestone: {
|
||||
daysOnMilestone: 60,
|
||||
warningPeriod: 10,
|
||||
numberOfCommentsToPreventAutomaticRejection: 20,
|
||||
rejectionComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!",
|
||||
warningComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding",
|
||||
perform: true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
name: Feature Request Manager
|
||||
on:
|
||||
repository_dispatch:
|
||||
issues:
|
||||
types: [labeled, milestoned]
|
||||
schedule:
|
||||
- cron: 20 2 * * * # 4:20am Zurich
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request')
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'JacksonKearl/vscode-triage-github-actions'
|
||||
ref: v20
|
||||
- name: Run Feature Request Manager
|
||||
if: github.event_name != 'issues' || contains(github.event.issue.labels.*.name, 'feature-request')
|
||||
uses: ./feature-request
|
||||
with:
|
||||
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
|
||||
candidateMilestoneID: 107
|
||||
candidateMilestoneName: Backlog Candidates
|
||||
backlogMilestoneID: 8
|
||||
featureRequestLabel: feature-request
|
||||
upvotesRequired: 20
|
||||
numCommentsOverride: 20
|
||||
initComment: "This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!"
|
||||
warnComment: "This feature request has not yet received the 20 community upvotes it takes to make to our backlog. 10 days to go. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding"
|
||||
acceptComment: ":slightly_smiling_face: This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!"
|
||||
rejectComment: ":slightly_frowning_face: In the last 60 days, this feature request has received less than 20 community upvotes and we closed it. Still a big Thank You to you for taking the time to create this issue! To learn more about how we handle feature requests, please see our [documentation](https://aka.ms/vscode-issue-lifecycle).\n\nHappy Coding!"
|
||||
warnDays: 10
|
||||
closeDays: 60
|
||||
milestoneDelaySeconds: 60
|
|
@ -2,7 +2,7 @@
|
|||
# Flags: CaseSensitive WordMatch
|
||||
# ContextLines: 2
|
||||
|
||||
11 results - 3 files
|
||||
9 results - 4 files
|
||||
|
||||
src/vs/base/common/arrays.ts:
|
||||
401
|
||||
|
@ -17,20 +17,14 @@ src/vs/base/common/arrays.ts:
|
|||
420 */
|
||||
421 export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T): T;
|
||||
|
||||
474
|
||||
475 /**
|
||||
476: * @deprecated ES6: use `Array.fill`
|
||||
477 */
|
||||
478 export function fill<T>(num: number, value: T, arr: T[] = []): T[] {
|
||||
|
||||
571
|
||||
572 /**
|
||||
573: * @deprecated ES6: use `Array.find`
|
||||
574 */
|
||||
575 export function find<T>(arr: ArrayLike<T>, predicate: (value: T, index: number, arr: ArrayLike<T>) => any): T | undefined {
|
||||
560
|
||||
561 /**
|
||||
562: * @deprecated ES6: use `Array.find`
|
||||
563 */
|
||||
564 export function find<T>(arr: ArrayLike<T>, predicate: (value: T, index: number, arr: ArrayLike<T>) => any): T | undefined {
|
||||
|
||||
src/vs/base/common/map.ts:
|
||||
9
|
||||
9
|
||||
10 /**
|
||||
11: * @deprecated ES6: use `[...SetOrMap.values()]`
|
||||
12 */
|
||||
|
@ -42,33 +36,28 @@ src/vs/base/common/map.ts:
|
|||
23 */
|
||||
24 export function keys<K, V>(map: Map<K, V>): K[] {
|
||||
|
||||
58
|
||||
59 /**
|
||||
60: * @deprecated ES6: use `...Map.entries()`
|
||||
61 */
|
||||
62 export function mapToSerializable(map: Map<string, string>): [string, string][] {
|
||||
|
||||
71
|
||||
72 /**
|
||||
73: * @deprecated ES6: use `new Map([[key1, value1],[key2, value2]])`
|
||||
74 */
|
||||
75 export function serializableToMap(serializable: [string, string][]): Map<string, string> {
|
||||
src/vs/base/common/objects.ts:
|
||||
115
|
||||
116 /**
|
||||
117: * @deprecated ES6
|
||||
118 */
|
||||
119 export function assign<T>(destination: T): T;
|
||||
|
||||
src/vs/base/common/strings.ts:
|
||||
16
|
||||
17 /**
|
||||
18: * @deprecated ES6: use `String.padStart`
|
||||
19 */
|
||||
20 export function pad(n: number, l: number, char: string = '0'): string {
|
||||
15
|
||||
16 /**
|
||||
17: * @deprecated ES6: use `String.padStart`
|
||||
18 */
|
||||
19 export function pad(n: number, l: number, char: string = '0'): string {
|
||||
|
||||
147
|
||||
148 /**
|
||||
149: * @deprecated ES6: use `String.startsWith`
|
||||
150 */
|
||||
151 export function startsWith(haystack: string, needle: string): boolean {
|
||||
146
|
||||
147 /**
|
||||
148: * @deprecated ES6: use `String.startsWith`
|
||||
149 */
|
||||
150 export function startsWith(haystack: string, needle: string): boolean {
|
||||
|
||||
168
|
||||
169 /**
|
||||
170: * @deprecated ES6: use `String.endsWith`
|
||||
171 */
|
||||
172 export function endsWith(haystack: string, needle: string): boolean {
|
||||
167
|
||||
168 /**
|
||||
169: * @deprecated ES6: use `String.endsWith`
|
||||
170 */
|
||||
171 export function endsWith(haystack: string, needle: string): boolean {
|
||||
|
|
|
@ -230,9 +230,9 @@ for-own@^0.1.4:
|
|||
for-in "^1.0.1"
|
||||
|
||||
fsevents@~2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9"
|
||||
integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw==
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805"
|
||||
integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==
|
||||
|
||||
glob-base@^0.3.0:
|
||||
version "0.3.0"
|
||||
|
@ -258,9 +258,9 @@ glob-parent@^3.0.1:
|
|||
path-dirname "^1.0.0"
|
||||
|
||||
glob-parent@~5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2"
|
||||
integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
|
@ -405,9 +405,9 @@ kind-of@^3.0.2:
|
|||
is-buffer "^1.1.5"
|
||||
|
||||
kind-of@^6.0.0:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
|
||||
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||
|
||||
math-random@^1.0.1:
|
||||
version "1.0.4"
|
||||
|
@ -479,9 +479,9 @@ path-is-absolute@^1.0.1:
|
|||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
picomatch@^2.0.4:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.0.tgz#0fd042f568d08b1ad9ff2d3ec0f0bfb3cb80e177"
|
||||
integrity sha512-uhnEDzAbrcJ8R3g2fANnSuXZMBtkpSjxTTgn2LeSiQlfmq72enQJWdQllXW24MBLYnA1SBD2vfvx2o0Zw3Ielw==
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
|
||||
pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
|
@ -530,9 +530,9 @@ randomatic@^3.0.0:
|
|||
math-random "^1.0.1"
|
||||
|
||||
readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.5:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
|
|
|
@ -1235,11 +1235,6 @@
|
|||
}
|
||||
],
|
||||
"timeline/item/context": [
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "inline",
|
||||
"when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "1_timeline",
|
||||
|
|
|
@ -74,7 +74,7 @@ export class GitTimelineProvider implements TimelineProvider {
|
|||
constructor(private readonly _model: Model) {
|
||||
this.disposable = Disposable.from(
|
||||
_model.onDidOpenRepository(this.onRepositoriesChanged, this),
|
||||
workspace.registerTimelineProvider(['file', 'git', 'gitlens-git'], this),
|
||||
workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git'], this),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as uuid from 'uuid';
|
||||
import { keychain } from './common/keychain';
|
||||
import { GitHubServer } from './githubServer';
|
||||
import Logger from './common/logger';
|
||||
|
@ -122,7 +123,7 @@ export class GitHubAuthenticationProvider {
|
|||
private async tokenToSession(token: string, scopes: string[]): Promise<vscode.AuthenticationSession> {
|
||||
const userInfo = await this._githubServer.getUserInfo(token);
|
||||
return {
|
||||
id: userInfo.id,
|
||||
id: uuid(),
|
||||
getAccessToken: () => Promise.resolve(token),
|
||||
accountName: userInfo.accountName,
|
||||
scopes: scopes
|
||||
|
|
|
@ -23,7 +23,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry);
|
||||
|
||||
context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager));
|
||||
context.subscriptions.push(vscode.window.registerCustomEditorProvider2(PreviewManager.viewType, previewManager));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => {
|
||||
previewManager.activePreview?.zoomIn();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"dependencies": {
|
||||
"jsonc-parser": "^2.2.1",
|
||||
"request-light": "^0.2.5",
|
||||
"vscode-json-languageservice": "^3.5.1",
|
||||
"vscode-json-languageservice": "^3.5.2",
|
||||
"vscode-languageserver": "^6.1.1",
|
||||
"vscode-uri": "^2.1.1"
|
||||
},
|
||||
|
|
|
@ -80,10 +80,10 @@ request-light@^0.2.5:
|
|||
https-proxy-agent "^2.2.3"
|
||||
vscode-nls "^4.1.1"
|
||||
|
||||
vscode-json-languageservice@^3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.5.1.tgz#75779d466107cbc8c4cc9828df100df71c1870f8"
|
||||
integrity sha512-F8jPqcAC1mbQOMKvGYS4dGEw9JCZxVEi7tc5ASZLfcfwKq2URZKB4fOtdy1GEsTLsrW11tVrBjEPntpXzqp/NA==
|
||||
vscode-json-languageservice@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.5.2.tgz#4b898140a8e581359c10660845a4cae15dcbb4f9"
|
||||
integrity sha512-9cUvBq00O08lpWVVOx6tQ1yLxCHss79nsUdEAVYGomRyMbnPBmc0AkYPcXI9WK1EM6HBo0R9Zo3NjFhcICpy4A==
|
||||
dependencies:
|
||||
jsonc-parser "^2.2.1"
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
|
|
|
@ -51,9 +51,11 @@
|
|||
"typescript": "^3.7.4",
|
||||
"tslint": "^5.12.1",
|
||||
"@types/node": "^10.12.21",
|
||||
"@types/keytar": "^4.0.1"
|
||||
"@types/keytar": "^4.0.1",
|
||||
"@types/uuid": "^3.4.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^3.3.3",
|
||||
"vscode-nls": "^4.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as crypto from 'crypto';
|
|||
import * as https from 'https';
|
||||
import * as querystring from 'querystring';
|
||||
import * as vscode from 'vscode';
|
||||
import * as uuid from 'uuid';
|
||||
import { createServer, startServer } from './authServer';
|
||||
import { keychain } from './keychain';
|
||||
import Logger from './logger';
|
||||
|
@ -81,7 +82,7 @@ export class AzureActiveDirectoryService {
|
|||
const sessions = this.parseStoredData(storedData);
|
||||
const refreshes = sessions.map(async session => {
|
||||
try {
|
||||
await this.refreshToken(session.refreshToken, session.scope);
|
||||
await this.refreshToken(session.refreshToken, session.scope, session.id);
|
||||
} catch (e) {
|
||||
if (e.message === REFRESH_NETWORK_FAILURE) {
|
||||
const didSucceedOnRetry = await this.handleRefreshNetworkError(session.id, session.refreshToken, session.scope);
|
||||
|
@ -140,7 +141,7 @@ export class AzureActiveDirectoryService {
|
|||
const matchesExisting = this._tokens.some(token => token.scope === session.scope && token.sessionId === session.id);
|
||||
if (!matchesExisting) {
|
||||
try {
|
||||
await this.refreshToken(session.refreshToken, session.scope);
|
||||
await this.refreshToken(session.refreshToken, session.scope, session.id);
|
||||
addedIds.push(session.id);
|
||||
} catch (e) {
|
||||
if (e.message === REFRESH_NETWORK_FAILURE) {
|
||||
|
@ -169,10 +170,17 @@ export class AzureActiveDirectoryService {
|
|||
}
|
||||
} else {
|
||||
if (this._tokens.length) {
|
||||
// Log out all
|
||||
// Log out all, remove all local data
|
||||
removedIds = this._tokens.map(token => token.sessionId);
|
||||
Logger.info('No tokens in memory, clearing keychain data');
|
||||
await this.clearSessions();
|
||||
Logger.info('No stored keychain data, clearing local data');
|
||||
|
||||
this._tokens = [];
|
||||
|
||||
this._refreshTimeouts.forEach(timeout => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
|
||||
this._refreshTimeouts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +211,7 @@ export class AzureActiveDirectoryService {
|
|||
|
||||
try {
|
||||
Logger.info('Token expired or unavailable, trying refresh');
|
||||
const refreshedToken = await this.refreshToken(token.refreshToken, token.scope);
|
||||
const refreshedToken = await this.refreshToken(token.refreshToken, token.scope, token.sessionId);
|
||||
if (refreshedToken.accessToken) {
|
||||
return refreshedToken.accessToken;
|
||||
} else {
|
||||
|
@ -379,7 +387,7 @@ export class AzureActiveDirectoryService {
|
|||
if (token.expiresIn) {
|
||||
this._refreshTimeouts.set(token.sessionId, setTimeout(async () => {
|
||||
try {
|
||||
await this.refreshToken(token.refreshToken, scope);
|
||||
await this.refreshToken(token.refreshToken, scope, token.sessionId);
|
||||
onDidChangeSessions.fire({ added: [], removed: [], changed: [token.sessionId] });
|
||||
} catch (e) {
|
||||
if (e.message === REFRESH_NETWORK_FAILURE) {
|
||||
|
@ -398,7 +406,7 @@ export class AzureActiveDirectoryService {
|
|||
this.storeTokenData();
|
||||
}
|
||||
|
||||
private getTokenFromResponse(buffer: Buffer[], scope: string): IToken {
|
||||
private getTokenFromResponse(buffer: Buffer[], scope: string, existingId?: string): IToken {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
const claims = this.getTokenClaims(json.access_token);
|
||||
return {
|
||||
|
@ -407,7 +415,7 @@ export class AzureActiveDirectoryService {
|
|||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token,
|
||||
scope,
|
||||
sessionId: `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${scope}`,
|
||||
sessionId: existingId || `${claims.tid}/${(claims.oid || (claims.altsecid || '' + claims.ipd || ''))}/${uuid()}`,
|
||||
accountName: claims.email || claims.unique_name || 'user@example.com'
|
||||
};
|
||||
}
|
||||
|
@ -465,7 +473,7 @@ export class AzureActiveDirectoryService {
|
|||
});
|
||||
}
|
||||
|
||||
private async refreshToken(refreshToken: string, scope: string): Promise<IToken> {
|
||||
private async refreshToken(refreshToken: string, scope: string, sessionId: string): Promise<IToken> {
|
||||
return new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
Logger.info('Refreshing token...');
|
||||
const postData = querystring.stringify({
|
||||
|
@ -490,7 +498,7 @@ export class AzureActiveDirectoryService {
|
|||
});
|
||||
result.on('end', async () => {
|
||||
if (result.statusCode === 200) {
|
||||
const token = this.getTokenFromResponse(buffer, scope);
|
||||
const token = this.getTokenFromResponse(buffer, scope, sessionId);
|
||||
this.setToken(token, scope);
|
||||
Logger.info('Token refresh success');
|
||||
resolve(token);
|
||||
|
@ -533,7 +541,7 @@ export class AzureActiveDirectoryService {
|
|||
|
||||
this._refreshTimeouts.set(sessionId, setTimeout(async () => {
|
||||
try {
|
||||
await this.refreshToken(refreshToken, scope);
|
||||
await this.refreshToken(refreshToken, scope, sessionId);
|
||||
} catch (e) {
|
||||
this.pollForReconnect(sessionId, refreshToken, scope);
|
||||
}
|
||||
|
@ -561,7 +569,7 @@ export class AzureActiveDirectoryService {
|
|||
|
||||
this._refreshTimeouts.set(sessionId, setTimeout(async () => {
|
||||
try {
|
||||
await this.refreshToken(refreshToken, scope);
|
||||
await this.refreshToken(refreshToken, scope, sessionId);
|
||||
return resolve(true);
|
||||
} catch (e) {
|
||||
return resolve(await this.handleRefreshNetworkError(sessionId, refreshToken, scope, attempts + 1));
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c"
|
||||
integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==
|
||||
|
||||
"@types/uuid@^3.4.6":
|
||||
version "3.4.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.8.tgz#4ba887fcef88bd9a7515ca2de336d691e3e18318"
|
||||
integrity sha512-zHWce3allXWSmRx6/AGXKCtSOA7JjeWd2L3t4aHfysNk8mouQnWCocveaT7a4IEIlPVHp81jzlnknqTgCjCLXA==
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
|
@ -635,6 +640,11 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@^3.3.3:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
vscode-nls@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"iconv-lite": "0.5.0",
|
||||
"jquery": "3.4.0",
|
||||
"jschardet": "2.1.1",
|
||||
"keytar": "^4.11.0",
|
||||
"keytar": "github:rmacfarlane/node-keytar#334424bd26414923782f144110f4beda19168d24",
|
||||
"minimist": "^1.2.5",
|
||||
"native-is-elevated": "0.4.1",
|
||||
"native-keymap": "2.1.1",
|
||||
|
@ -185,7 +185,7 @@
|
|||
"vinyl": "^2.0.0",
|
||||
"vinyl-fs": "^3.0.0",
|
||||
"vsce": "1.48.0",
|
||||
"vscode-debugprotocol": "1.40.0-pre.1",
|
||||
"vscode-debugprotocol": "^1.40.0",
|
||||
"vscode-nls-dev": "^3.3.1",
|
||||
"webpack": "^4.16.5",
|
||||
"webpack-cli": "^3.3.8",
|
||||
|
|
|
@ -8,26 +8,29 @@ import { QUERY_HISTORY_PANEL_ID } from 'sql/workbench/contrib/queryHistory/commo
|
|||
import { RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQueryHistoryService } from 'sql/workbench/services/queryHistory/common/queryHistoryService';
|
||||
import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { openNewQuery } from 'sql/workbench/contrib/query/browser/queryActions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
|
||||
export class ToggleQueryHistoryAction extends TogglePanelAction {
|
||||
export class ToggleQueryHistoryAction extends ToggleViewAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.tasks.toggleQueryHistory';
|
||||
public static readonly LABEL = localize('toggleQueryHistory', "Toggle Query History");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IViewsService viewsService: IViewsService,
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label, QUERY_HISTORY_PANEL_ID, panelService, layoutService);
|
||||
super(id, label, QUERY_HISTORY_PANEL_ID, viewsService, viewDescriptorService, contextKeyService, layoutService);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,20 +5,23 @@
|
|||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { TogglePanelAction } from 'vs/workbench/browser/panel';
|
||||
import { ToggleViewAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TASKS_PANEL_ID } from 'sql/workbench/contrib/tasks/common/tasks';
|
||||
|
||||
export class ToggleTasksAction extends TogglePanelAction {
|
||||
export class ToggleTasksAction extends ToggleViewAction {
|
||||
|
||||
public static readonly ID = 'workbench.action.tasks.toggleTasks';
|
||||
public static readonly LABEL = localize('toggleTasks', "Toggle Tasks");
|
||||
|
||||
constructor(
|
||||
id: string, label: string,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IViewsService viewsService: IViewsService,
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label, TASKS_PANEL_ID, panelService, layoutService);
|
||||
super(id, label, TASKS_PANEL_ID, viewsService, viewDescriptorService, contextKeyService, layoutService);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,11 @@ export let addStandardDisposableGenericMouseDownListner = function addStandardDi
|
|||
return addDisposableGenericMouseDownListner(node, wrapHandler, useCapture);
|
||||
};
|
||||
|
||||
export let addStandardDisposableGenericMouseUpListner = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
let wrapHandler = _wrapAsStandardMouseEvent(handler);
|
||||
|
||||
return addDisposableGenericMouseUpListner(node, wrapHandler, useCapture);
|
||||
};
|
||||
export function addDisposableGenericMouseDownListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_DOWN : EventType.MOUSE_DOWN, handler, useCapture);
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69064L6.33332 3.02397L5.7146 3.64269L10.0719 7.99999Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 284 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69063L6.33332 3.02396L5.7146 3.64268L10.0719 7.99999Z" fill="white"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 282 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.7146 12.3573L6.33332 12.976L11 8.30935V7.69063L6.33332 3.02396L5.7146 3.64268L10.0719 7.99999Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 284 B |
|
@ -36,7 +36,7 @@ export class HorizontalScrollbar extends AbstractScrollbar {
|
|||
let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2;
|
||||
|
||||
this._createArrow({
|
||||
className: 'left-arrow',
|
||||
className: 'scra codicon codicon-triangle-left',
|
||||
top: scrollbarDelta,
|
||||
left: arrowDelta,
|
||||
bottom: undefined,
|
||||
|
@ -47,7 +47,7 @@ export class HorizontalScrollbar extends AbstractScrollbar {
|
|||
});
|
||||
|
||||
this._createArrow({
|
||||
className: 'right-arrow',
|
||||
className: 'scra codicon codicon-triangle-right',
|
||||
top: scrollbarDelta,
|
||||
left: undefined,
|
||||
bottom: undefined,
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-180 5.49045991897583,5.811500072479248)" fill="#E8E8E8" d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 233 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-180 5.49045991897583,5.811500072479248)" fill="#424242" d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 233 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-90 5.490459918975831,5.431382179260254)" fill="#E8E8E8" d="m9.48046,8.58138l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 234 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11"><path transform="rotate(-90 5.490459918975831,5.431382179260254)" fill="#424242" d="m9.48046,8.58138l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 234 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path transform="rotate(90 5.6171650886535645,5.55808973312378) " fill="#E8E8E8" d="m9.60717,8.70809l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 234 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path transform="rotate(90 5.6171650886535645,5.55808973312378) " fill="#424242" d="m9.60717,8.70809l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z"/></svg>
|
До Ширина: | Высота: | Размер: 234 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z" fill="#E8E8E8"/></svg>
|
До Ширина: | Высота: | Размер: 173 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg"><path d="m9.48046,8.9615l1.26,-1.26l-5.04,-5.04l-5.46,5.04l1.26,1.26l4.2,-3.78l3.78,3.78z" fill="#424242"/></svg>
|
До Ширина: | Высота: | Размер: 173 B |
|
@ -4,38 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Arrows */
|
||||
.monaco-scrollable-element > .scrollbar > .up-arrow {
|
||||
background: url('arrow-up.svg');
|
||||
.monaco-scrollable-element > .scrollbar > .scra {
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .down-arrow {
|
||||
background: url('arrow-down.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .left-arrow {
|
||||
background: url('arrow-left.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
.monaco-scrollable-element > .scrollbar > .right-arrow {
|
||||
background: url('arrow-right.svg');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .up-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .up-arrow {
|
||||
background: url('arrow-up-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .down-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .down-arrow {
|
||||
background: url('arrow-down-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .left-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .left-arrow {
|
||||
background: url('arrow-left-dark.svg');
|
||||
}
|
||||
.hc-black .monaco-scrollable-element > .scrollbar > .right-arrow,
|
||||
.vs-dark .monaco-scrollable-element > .scrollbar > .right-arrow {
|
||||
background: url('arrow-right-dark.svg');
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
.monaco-scrollable-element > .visible {
|
||||
|
@ -137,4 +108,4 @@
|
|||
|
||||
.hc-black .monaco-scrollable-element .shadow.top.left {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ export class VerticalScrollbar extends AbstractScrollbar {
|
|||
let scrollbarDelta = (options.verticalScrollbarSize - ARROW_IMG_SIZE) / 2;
|
||||
|
||||
this._createArrow({
|
||||
className: 'up-arrow',
|
||||
className: 'scra codicon codicon-triangle-up',
|
||||
top: arrowDelta,
|
||||
left: scrollbarDelta,
|
||||
bottom: undefined,
|
||||
|
@ -48,7 +48,7 @@ export class VerticalScrollbar extends AbstractScrollbar {
|
|||
});
|
||||
|
||||
this._createArrow({
|
||||
className: 'down-arrow',
|
||||
className: 'scra codicon codicon-triangle-down',
|
||||
top: undefined,
|
||||
left: scrollbarDelta,
|
||||
bottom: arrowDelta,
|
||||
|
|
|
@ -52,6 +52,10 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate {
|
|||
}));
|
||||
});
|
||||
|
||||
this._register(dom.addStandardDisposableListener(this.selectElement, 'click', (e) => {
|
||||
dom.EventHelper.stop(e, true);
|
||||
}));
|
||||
|
||||
this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => {
|
||||
this.selectElement.title = e.target.value;
|
||||
this._onDidSelect.fire({
|
||||
|
|
|
@ -68,6 +68,24 @@
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane > .pane-header .monaco-action-bar .action-item.select-container {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-pane-view .pane > .pane-header .action-item .monaco-select-box {
|
||||
cursor: pointer;
|
||||
min-width: 110px;
|
||||
min-height: 18px;
|
||||
padding: 2px 23px 2px 8px;
|
||||
background-color: inherit !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.linux .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box,
|
||||
.windows .monaco-pane-view .pane > .pane-header .action-item .monaco-select-box {
|
||||
padding: 0px 23px 2px 8px;
|
||||
}
|
||||
|
||||
/* Bold font style does not go well with CJK fonts */
|
||||
.monaco-pane-view:lang(zh-Hans) .pane > .pane-header,
|
||||
.monaco-pane-view:lang(zh-Hant) .pane > .pane-header,
|
||||
|
|
|
@ -31,6 +31,7 @@ export interface IPaneStyles {
|
|||
headerForeground?: Color;
|
||||
headerBackground?: Color;
|
||||
headerBorder?: Color;
|
||||
leftBorder?: Color;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,6 +244,8 @@ export abstract class Pane extends Disposable implements IView {
|
|||
style(styles: IPaneStyles): void {
|
||||
this.styles = styles;
|
||||
|
||||
this.element.style.borderLeft = this.styles.leftBorder && this.orientation === Orientation.HORIZONTAL ? `1px solid ${this.styles.leftBorder}` : '';
|
||||
|
||||
if (!this.header) {
|
||||
return;
|
||||
}
|
||||
|
@ -261,7 +264,7 @@ export abstract class Pane extends Disposable implements IView {
|
|||
|
||||
this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : '';
|
||||
this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : '';
|
||||
this.header.style.borderTop = this.styles.headerBorder ? `1px solid ${this.styles.headerBorder}` : '';
|
||||
this.header.style.borderTop = this.styles.headerBorder && this.orientation === Orientation.VERTICAL ? `1px solid ${this.styles.headerBorder}` : '';
|
||||
this._dropBackground = this.styles.dropBackground;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version='1.0' standalone='no' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
|
||||
<style>
|
||||
circle {
|
||||
animation: ball 0.6s linear infinite;
|
||||
}
|
||||
|
||||
circle:nth-child(2) { animation-delay: 0.075s; }
|
||||
circle:nth-child(3) { animation-delay: 0.15s; }
|
||||
circle:nth-child(4) { animation-delay: 0.225s; }
|
||||
circle:nth-child(5) { animation-delay: 0.3s; }
|
||||
circle:nth-child(6) { animation-delay: 0.375s; }
|
||||
circle:nth-child(7) { animation-delay: 0.45s; }
|
||||
circle:nth-child(8) { animation-delay: 0.525s; }
|
||||
|
||||
@keyframes ball {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0.3; }
|
||||
}
|
||||
</style>
|
||||
<g style="fill:grey;">
|
||||
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
</g>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1,31 +0,0 @@
|
|||
<?xml version='1.0' standalone='no' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
|
||||
<style>
|
||||
circle {
|
||||
animation: ball 0.6s linear infinite;
|
||||
}
|
||||
|
||||
circle:nth-child(2) { animation-delay: 0.075s; }
|
||||
circle:nth-child(3) { animation-delay: 0.15s; }
|
||||
circle:nth-child(4) { animation-delay: 0.225s; }
|
||||
circle:nth-child(5) { animation-delay: 0.3s; }
|
||||
circle:nth-child(6) { animation-delay: 0.375s; }
|
||||
circle:nth-child(7) { animation-delay: 0.45s; }
|
||||
circle:nth-child(8) { animation-delay: 0.525s; }
|
||||
|
||||
@keyframes ball {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0.3; }
|
||||
}
|
||||
</style>
|
||||
<g style="fill:white;">
|
||||
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
</g>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1,31 +0,0 @@
|
|||
<?xml version='1.0' standalone='no' ?>
|
||||
<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='10px' height='10px'>
|
||||
<style>
|
||||
circle {
|
||||
animation: ball 0.6s linear infinite;
|
||||
}
|
||||
|
||||
circle:nth-child(2) { animation-delay: 0.075s; }
|
||||
circle:nth-child(3) { animation-delay: 0.15s; }
|
||||
circle:nth-child(4) { animation-delay: 0.225s; }
|
||||
circle:nth-child(5) { animation-delay: 0.3s; }
|
||||
circle:nth-child(6) { animation-delay: 0.375s; }
|
||||
circle:nth-child(7) { animation-delay: 0.45s; }
|
||||
circle:nth-child(8) { animation-delay: 0.525s; }
|
||||
|
||||
@keyframes ball {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0.3; }
|
||||
}
|
||||
</style>
|
||||
<g>
|
||||
<circle cx='5' cy='1' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
<circle cx='9' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='7.8284' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='5' cy='9' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='7.8284' r='1' style='opacity:0.3;' />
|
||||
<circle cx='1' cy='5' r='1' style='opacity:0.3;' />
|
||||
<circle cx='2.1716' cy='2.1716' r='1' style='opacity:0.3;' />
|
||||
</g>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69064L6.33333 3.02397L5.71461 3.64269L10.0719 7.99999Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 286 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69063L6.33333 3.02396L5.71461 3.64268L10.0719 7.99999Z" fill="white"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 284 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0719 7.99999L5.71461 12.3573L6.33333 12.976L11 8.30935V7.69063L6.33333 3.02396L5.71461 3.64268L10.0719 7.99999Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 286 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 284 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="white"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 282 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.97603 10.0719L12.3333 5.71461L12.9521 6.33333L8.28539 11L7.66667 11L3 6.33333L3.61872 5.71461L7.97603 10.0719Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 284 B |
|
@ -4,56 +4,24 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { matchesPrefix, IMatch, matchesCamelCase, isUpper } from 'vs/base/common/filters';
|
||||
import { matchesPrefix, IMatch, matchesCamelCase, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { distinctES6 } from 'vs/base/common/arrays';
|
||||
|
||||
export type Score = [number /* score */, number[] /* match positions */];
|
||||
export type ScorerCache = { [key: string]: IItemScore };
|
||||
//#region Fuzzy scorer
|
||||
|
||||
export type FuzzyScore = [number /* score */, number[] /* match positions */];
|
||||
export type FuzzyScorerCache = { [key: string]: IItemScore };
|
||||
|
||||
const NO_MATCH = 0;
|
||||
const NO_SCORE: Score = [NO_MATCH, []];
|
||||
const NO_SCORE: FuzzyScore = [NO_MATCH, []];
|
||||
|
||||
// const DEBUG = false;
|
||||
// const DEBUG_MATRIX = false;
|
||||
|
||||
export function score(target: string, query: IPreparedQuery, fuzzy: boolean): Score {
|
||||
if (query.values && query.values.length > 1) {
|
||||
return scoreMultiple(target, query.values, fuzzy);
|
||||
}
|
||||
|
||||
return scoreSingle(target, query.normalized, query.normalizedLowercase, fuzzy);
|
||||
}
|
||||
|
||||
function scoreMultiple(target: string, query: IPreparedQueryPiece[], fuzzy: boolean): Score {
|
||||
let totalScore = NO_MATCH;
|
||||
const totalPositions: number[] = [];
|
||||
|
||||
for (const { normalized, normalizedLowercase } of query) {
|
||||
const [scoreValue, positions] = scoreSingle(target, normalized, normalizedLowercase, fuzzy);
|
||||
if (scoreValue === NO_MATCH) {
|
||||
// if a single query value does not match, return with
|
||||
// no score entirely, we require all queries to match
|
||||
return NO_SCORE;
|
||||
}
|
||||
|
||||
totalScore += scoreValue;
|
||||
totalPositions.push(...positions);
|
||||
}
|
||||
|
||||
if (totalScore === NO_MATCH) {
|
||||
return NO_SCORE;
|
||||
}
|
||||
|
||||
// if we have a score, ensure that the positions are
|
||||
// sorted in ascending order and distinct
|
||||
return [totalScore, distinctES6(totalPositions).sort((a, b) => a - b)];
|
||||
}
|
||||
|
||||
function scoreSingle(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
|
||||
export function scoreFuzzy(target: string, query: string, queryLower: string, fuzzy: boolean): FuzzyScore {
|
||||
if (!target || !query) {
|
||||
return NO_SCORE; // return early if target or query are undefined
|
||||
}
|
||||
|
@ -84,7 +52,7 @@ function scoreSingle(target: string, query: string, queryLower: string, fuzzy: b
|
|||
}
|
||||
}
|
||||
|
||||
const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength);
|
||||
const res = doScoreFuzzy(query, queryLower, queryLength, target, targetLower, targetLength);
|
||||
|
||||
// if (DEBUG) {
|
||||
// console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold');
|
||||
|
@ -94,7 +62,7 @@ function scoreSingle(target: string, query: string, queryLower: string, fuzzy: b
|
|||
return res;
|
||||
}
|
||||
|
||||
function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): Score {
|
||||
function doScoreFuzzy(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): FuzzyScore {
|
||||
const scores: number[] = [];
|
||||
const matches: number[] = [];
|
||||
|
||||
|
@ -291,6 +259,61 @@ function scoreSeparatorAtPos(charCode: number): number {
|
|||
// }
|
||||
// }
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Alternate fuzzy scorer implementation that is e.g. used for symbols
|
||||
|
||||
export type FuzzyScore2 = [number /* score*/, IMatch[]];
|
||||
|
||||
const NO_SCORE2: FuzzyScore2 = [NO_MATCH, []];
|
||||
|
||||
export function scoreFuzzy2(target: string, query: IPreparedQuery, patternStart = 0, matchOffset = 0): FuzzyScore2 {
|
||||
|
||||
// Score: multiple inputs
|
||||
if (query.values && query.values.length > 1) {
|
||||
return doScoreFuzzy2Multiple(target, query.values, patternStart, matchOffset);
|
||||
}
|
||||
|
||||
// Score: single input
|
||||
return doScoreFuzzy2Single(target, query, patternStart, matchOffset);
|
||||
}
|
||||
|
||||
function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], patternStart: number, matchOffset: number): FuzzyScore2 {
|
||||
let totalScore = 0;
|
||||
const totalMatches: IMatch[] = [];
|
||||
|
||||
for (const queryPiece of query) {
|
||||
const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, matchOffset);
|
||||
if (!score) {
|
||||
// if a single query value does not match, return with
|
||||
// no score entirely, we require all queries to match
|
||||
return NO_SCORE2;
|
||||
}
|
||||
|
||||
totalScore += score;
|
||||
totalMatches.push(...matches);
|
||||
}
|
||||
|
||||
// if we have a score, ensure that the positions are
|
||||
// sorted in ascending order and distinct
|
||||
return [totalScore, normalizeMatches(totalMatches)];
|
||||
}
|
||||
|
||||
function doScoreFuzzy2Single(target: string, query: IPreparedQueryPiece, patternStart: number, matchOffset: number): FuzzyScore2 {
|
||||
const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), 0, true);
|
||||
if (!score) {
|
||||
return NO_SCORE2;
|
||||
}
|
||||
|
||||
return [score[0], createFuzzyMatches(score, matchOffset)];
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Item (label, description, path) scorer
|
||||
|
||||
/**
|
||||
* Scoring on structural items that have a label and optional description.
|
||||
*/
|
||||
|
@ -337,99 +360,7 @@ const LABEL_PREFIX_SCORE = 1 << 17;
|
|||
const LABEL_CAMELCASE_SCORE = 1 << 16;
|
||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||
|
||||
export interface IPreparedQueryPiece {
|
||||
|
||||
/**
|
||||
* The original query as provided as input.
|
||||
*/
|
||||
original: string;
|
||||
originalLowercase: string;
|
||||
|
||||
/**
|
||||
* Original normalized to platform separators:
|
||||
* - Windows: \
|
||||
* - Posix: /
|
||||
*/
|
||||
pathNormalized: string;
|
||||
|
||||
/**
|
||||
* In addition to the normalized path, will have
|
||||
* whitespace and wildcards removed.
|
||||
*/
|
||||
normalized: string;
|
||||
normalizedLowercase: string;
|
||||
}
|
||||
|
||||
export interface IPreparedQuery extends IPreparedQueryPiece {
|
||||
|
||||
// Split by spaces
|
||||
values: IPreparedQueryPiece[] | undefined;
|
||||
|
||||
containsPathSeparator: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare a search value for scoring by removing unwanted characters
|
||||
* and allowing to score on multiple pieces separated by whitespace character.
|
||||
*/
|
||||
const MULTIPL_QUERY_VALUES_SEPARATOR = ' ';
|
||||
export function prepareQuery(original: string): IPreparedQuery {
|
||||
if (typeof original !== 'string') {
|
||||
original = '';
|
||||
}
|
||||
|
||||
const originalLowercase = original.toLowerCase();
|
||||
const { pathNormalized, normalized, normalizedLowercase } = normalizeQuery(original);
|
||||
const containsPathSeparator = pathNormalized.indexOf(sep) >= 0;
|
||||
|
||||
let values: IPreparedQueryPiece[] | undefined = undefined;
|
||||
|
||||
const originalSplit = original.split(MULTIPL_QUERY_VALUES_SEPARATOR);
|
||||
if (originalSplit.length > 1) {
|
||||
for (const originalPiece of originalSplit) {
|
||||
const {
|
||||
pathNormalized: pathNormalizedPiece,
|
||||
normalized: normalizedPiece,
|
||||
normalizedLowercase: normalizedLowercasePiece
|
||||
} = normalizeQuery(originalPiece);
|
||||
|
||||
if (normalizedPiece) {
|
||||
if (!values) {
|
||||
values = [];
|
||||
}
|
||||
|
||||
values.push({
|
||||
original: originalPiece,
|
||||
originalLowercase: originalPiece.toLowerCase(),
|
||||
pathNormalized: pathNormalizedPiece,
|
||||
normalized: normalizedPiece,
|
||||
normalizedLowercase: normalizedLowercasePiece
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { original, originalLowercase, pathNormalized, normalized, normalizedLowercase, values, containsPathSeparator };
|
||||
}
|
||||
|
||||
function normalizeQuery(original: string): { pathNormalized: string, normalized: string, normalizedLowercase: string } {
|
||||
let pathNormalized: string;
|
||||
if (isWindows) {
|
||||
pathNormalized = original.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||
} else {
|
||||
pathNormalized = original.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
||||
}
|
||||
|
||||
const normalized = stripWildcards(pathNormalized).replace(/\s/g, '');
|
||||
|
||||
return {
|
||||
pathNormalized,
|
||||
normalized,
|
||||
normalizedLowercase: normalized.toLowerCase()
|
||||
};
|
||||
}
|
||||
|
||||
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||
export function scoreItemFuzzy<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache): IItemScore {
|
||||
if (!item || !query.normalized) {
|
||||
return NO_ITEM_SCORE; // we need an item and query to score on at least
|
||||
}
|
||||
|
@ -453,62 +384,86 @@ export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, acc
|
|||
return cached;
|
||||
}
|
||||
|
||||
const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy);
|
||||
const itemScore = doScoreItemFuzzy(label, description, accessor.getItemPath(item), query, fuzzy);
|
||||
cache[cacheHash] = itemScore;
|
||||
|
||||
return itemScore;
|
||||
}
|
||||
|
||||
function createMatches(offsets: undefined | number[]): IMatch[] {
|
||||
let ret: IMatch[] = [];
|
||||
if (!offsets) {
|
||||
return ret;
|
||||
}
|
||||
function doScoreItemFuzzy(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||
const preferLabelMatches = !path || !query.containsPathSeparator;
|
||||
|
||||
let last: IMatch | undefined;
|
||||
for (const pos of offsets) {
|
||||
if (last && last.end === pos) {
|
||||
last.end += 1;
|
||||
} else {
|
||||
last = { start: pos, end: pos + 1 };
|
||||
ret.push(last);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function doScoreItem(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||
|
||||
// 1.) treat identity matches on full path highest
|
||||
// Treat identity matches on full path highest
|
||||
if (path && (isLinux ? query.pathNormalized === path : equalsIgnoreCase(query.pathNormalized, path))) {
|
||||
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined };
|
||||
}
|
||||
|
||||
// We only consider label matches if the query is not including file path separators
|
||||
const preferLabelMatches = !path || !query.containsPathSeparator;
|
||||
// Score: multiple inputs
|
||||
if (query.values && query.values.length > 1) {
|
||||
return doScoreItemFuzzyMultiple(label, description, path, query.values, preferLabelMatches, fuzzy);
|
||||
}
|
||||
|
||||
// Score: single input
|
||||
return doScoreItemFuzzySingle(label, description, path, query, preferLabelMatches, fuzzy);
|
||||
}
|
||||
|
||||
function doScoreItemFuzzyMultiple(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece[], preferLabelMatches: boolean, fuzzy: boolean): IItemScore {
|
||||
let totalScore = 0;
|
||||
const totalLabelMatches: IMatch[] = [];
|
||||
const totalDescriptionMatches: IMatch[] = [];
|
||||
|
||||
for (const queryPiece of query) {
|
||||
const { score, labelMatch, descriptionMatch } = doScoreItemFuzzySingle(label, description, path, queryPiece, preferLabelMatches, fuzzy);
|
||||
if (score === NO_MATCH) {
|
||||
// if a single query value does not match, return with
|
||||
// no score entirely, we require all queries to match
|
||||
return NO_ITEM_SCORE;
|
||||
}
|
||||
|
||||
totalScore += score;
|
||||
if (labelMatch) {
|
||||
totalLabelMatches.push(...labelMatch);
|
||||
}
|
||||
|
||||
if (descriptionMatch) {
|
||||
totalDescriptionMatches.push(...descriptionMatch);
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a score, ensure that the positions are
|
||||
// sorted in ascending order and distinct
|
||||
return {
|
||||
score: totalScore,
|
||||
labelMatch: normalizeMatches(totalLabelMatches),
|
||||
descriptionMatch: normalizeMatches(totalDescriptionMatches)
|
||||
};
|
||||
}
|
||||
|
||||
function doScoreItemFuzzySingle(label: string, description: string | undefined, path: string | undefined, query: IPreparedQueryPiece, preferLabelMatches: boolean, fuzzy: boolean): IItemScore {
|
||||
|
||||
// Prefer label matches if told so
|
||||
if (preferLabelMatches) {
|
||||
|
||||
// 2.) treat prefix matches on the label second highest
|
||||
// Treat prefix matches on the label second highest
|
||||
const prefixLabelMatch = matchesPrefix(query.normalized, label);
|
||||
if (prefixLabelMatch) {
|
||||
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
|
||||
}
|
||||
|
||||
// 3.) treat camelcase matches on the label third highest
|
||||
// Treat camelcase matches on the label third highest
|
||||
const camelcaseLabelMatch = matchesCamelCase(query.normalized, label);
|
||||
if (camelcaseLabelMatch) {
|
||||
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
|
||||
}
|
||||
|
||||
// 4.) prefer scores on the label if any
|
||||
const [labelScore, labelPositions] = score(label, query, fuzzy);
|
||||
// Prefer scores on the label if any
|
||||
const [labelScore, labelPositions] = scoreFuzzy(label, query.normalized, query.normalizedLowercase, fuzzy);
|
||||
if (labelScore) {
|
||||
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
|
||||
}
|
||||
}
|
||||
|
||||
// 5.) finally compute description + label scores if we have a description
|
||||
// Finally compute description + label scores if we have a description
|
||||
if (description) {
|
||||
let descriptionPrefix = description;
|
||||
if (!!path) {
|
||||
|
@ -518,7 +473,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
|
|||
const descriptionPrefixLength = descriptionPrefix.length;
|
||||
const descriptionAndLabel = `${descriptionPrefix}${label}`;
|
||||
|
||||
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query, fuzzy);
|
||||
const [labelDescriptionScore, labelDescriptionPositions] = scoreFuzzy(descriptionAndLabel, query.normalized, query.normalizedLowercase, fuzzy);
|
||||
if (labelDescriptionScore) {
|
||||
const labelDescriptionMatches = createMatches(labelDescriptionPositions);
|
||||
const labelMatch: IMatch[] = [];
|
||||
|
@ -551,9 +506,45 @@ function doScoreItem(label: string, description: string | undefined, path: strin
|
|||
return NO_ITEM_SCORE;
|
||||
}
|
||||
|
||||
export function compareItemsByScore<T>(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): number {
|
||||
const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache);
|
||||
const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache);
|
||||
function createMatches(offsets: number[] | undefined): IMatch[] {
|
||||
const ret: IMatch[] = [];
|
||||
if (!offsets) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
let last: IMatch | undefined;
|
||||
for (const pos of offsets) {
|
||||
if (last && last.end === pos) {
|
||||
last.end += 1;
|
||||
} else {
|
||||
last = { start: pos, end: pos + 1 };
|
||||
ret.push(last);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function normalizeMatches(matches: IMatch[]): IMatch[] {
|
||||
const positions = new Set<number>();
|
||||
|
||||
for (const match of matches) {
|
||||
for (let i = match.start; i < match.end; i++) {
|
||||
positions.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return createMatches(Array.from(positions.values()).sort((a, b) => a - b));
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Comparers
|
||||
|
||||
export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache): number {
|
||||
const itemScoreA = scoreItemFuzzy(itemA, query, fuzzy, accessor, cache);
|
||||
const itemScoreB = scoreItemFuzzy(itemB, query, fuzzy, accessor, cache);
|
||||
|
||||
const scoreA = itemScoreA.score;
|
||||
const scoreB = itemScoreB.score;
|
||||
|
@ -744,3 +735,112 @@ function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor:
|
|||
// equal
|
||||
return 0;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Query Normalizer
|
||||
|
||||
export interface IPreparedQueryPiece {
|
||||
|
||||
/**
|
||||
* The original query as provided as input.
|
||||
*/
|
||||
original: string;
|
||||
originalLowercase: string;
|
||||
|
||||
/**
|
||||
* Original normalized to platform separators:
|
||||
* - Windows: \
|
||||
* - Posix: /
|
||||
*/
|
||||
pathNormalized: string;
|
||||
|
||||
/**
|
||||
* In addition to the normalized path, will have
|
||||
* whitespace and wildcards removed.
|
||||
*/
|
||||
normalized: string;
|
||||
normalizedLowercase: string;
|
||||
}
|
||||
|
||||
export interface IPreparedQuery extends IPreparedQueryPiece {
|
||||
|
||||
// Split by spaces
|
||||
values: IPreparedQueryPiece[] | undefined;
|
||||
|
||||
containsPathSeparator: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to prepare a search value for scoring by removing unwanted characters
|
||||
* and allowing to score on multiple pieces separated by whitespace character.
|
||||
*/
|
||||
const MULTIPLE_QUERY_VALUES_SEPARATOR = ' ';
|
||||
export function prepareQuery(original: string): IPreparedQuery {
|
||||
if (typeof original !== 'string') {
|
||||
original = '';
|
||||
}
|
||||
|
||||
const originalLowercase = original.toLowerCase();
|
||||
const { pathNormalized, normalized, normalizedLowercase } = normalizeQuery(original);
|
||||
const containsPathSeparator = pathNormalized.indexOf(sep) >= 0;
|
||||
|
||||
let values: IPreparedQueryPiece[] | undefined = undefined;
|
||||
|
||||
const originalSplit = original.split(MULTIPLE_QUERY_VALUES_SEPARATOR);
|
||||
if (originalSplit.length > 1) {
|
||||
for (const originalPiece of originalSplit) {
|
||||
const {
|
||||
pathNormalized: pathNormalizedPiece,
|
||||
normalized: normalizedPiece,
|
||||
normalizedLowercase: normalizedLowercasePiece
|
||||
} = normalizeQuery(originalPiece);
|
||||
|
||||
if (normalizedPiece) {
|
||||
if (!values) {
|
||||
values = [];
|
||||
}
|
||||
|
||||
values.push({
|
||||
original: originalPiece,
|
||||
originalLowercase: originalPiece.toLowerCase(),
|
||||
pathNormalized: pathNormalizedPiece,
|
||||
normalized: normalizedPiece,
|
||||
normalizedLowercase: normalizedLowercasePiece
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { original, originalLowercase, pathNormalized, normalized, normalizedLowercase, values, containsPathSeparator };
|
||||
}
|
||||
|
||||
function normalizeQuery(original: string): { pathNormalized: string, normalized: string, normalizedLowercase: string } {
|
||||
let pathNormalized: string;
|
||||
if (isWindows) {
|
||||
pathNormalized = original.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||
} else {
|
||||
pathNormalized = original.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
||||
}
|
||||
|
||||
const normalized = stripWildcards(pathNormalized).replace(/\s/g, '');
|
||||
|
||||
return {
|
||||
pathNormalized,
|
||||
normalized,
|
||||
normalizedLowercase: normalized.toLowerCase()
|
||||
};
|
||||
}
|
||||
|
||||
export function pieceToQuery(piece: IPreparedQueryPiece): IPreparedQuery;
|
||||
export function pieceToQuery(pieces: IPreparedQueryPiece[]): IPreparedQuery;
|
||||
export function pieceToQuery(arg1: IPreparedQueryPiece | IPreparedQueryPiece[]): IPreparedQuery {
|
||||
if (Array.isArray(arg1)) {
|
||||
return prepareQuery(arg1.map(piece => piece.original).join(MULTIPLE_QUERY_VALUES_SEPARATOR));
|
||||
}
|
||||
|
||||
return prepareQuery(arg1.original);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -113,6 +113,9 @@ export function mixin(destination: any, source: any, overwrite: boolean = true):
|
|||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated ES6
|
||||
*/
|
||||
export function assign<T>(destination: T): T;
|
||||
export function assign<T, U>(destination: T, u: U): T & U;
|
||||
export function assign<T, U, V>(destination: T, u: U, v: V): T & U & V;
|
||||
|
|
|
@ -33,6 +33,9 @@ export interface ScrollEvent {
|
|||
export class ScrollState implements IScrollDimensions, IScrollPosition {
|
||||
_scrollStateBrand: void;
|
||||
|
||||
public readonly rawScrollLeft: number;
|
||||
public readonly rawScrollTop: number;
|
||||
|
||||
public readonly width: number;
|
||||
public readonly scrollWidth: number;
|
||||
public readonly scrollLeft: number;
|
||||
|
@ -55,6 +58,9 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
|||
scrollHeight = scrollHeight | 0;
|
||||
scrollTop = scrollTop | 0;
|
||||
|
||||
this.rawScrollLeft = scrollLeft; // before validation
|
||||
this.rawScrollTop = scrollTop; // before validation
|
||||
|
||||
if (width < 0) {
|
||||
width = 0;
|
||||
}
|
||||
|
@ -85,7 +91,9 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
|||
|
||||
public equals(other: ScrollState): boolean {
|
||||
return (
|
||||
this.width === other.width
|
||||
this.rawScrollLeft === other.rawScrollLeft
|
||||
&& this.rawScrollTop === other.rawScrollTop
|
||||
&& this.width === other.width
|
||||
&& this.scrollWidth === other.scrollWidth
|
||||
&& this.scrollLeft === other.scrollLeft
|
||||
&& this.height === other.height
|
||||
|
@ -98,10 +106,10 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
|||
return new ScrollState(
|
||||
(typeof update.width !== 'undefined' ? update.width : this.width),
|
||||
(typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth),
|
||||
this.scrollLeft,
|
||||
this.rawScrollLeft,
|
||||
(typeof update.height !== 'undefined' ? update.height : this.height),
|
||||
(typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight),
|
||||
this.scrollTop
|
||||
this.rawScrollTop
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -109,10 +117,10 @@ export class ScrollState implements IScrollDimensions, IScrollPosition {
|
|||
return new ScrollState(
|
||||
this.width,
|
||||
this.scrollWidth,
|
||||
(typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.scrollLeft),
|
||||
(typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.rawScrollLeft),
|
||||
this.height,
|
||||
this.scrollHeight,
|
||||
(typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.scrollTop)
|
||||
(typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.rawScrollTop)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,45 +5,25 @@
|
|||
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
||||
const _typeof = {
|
||||
number: 'number',
|
||||
string: 'string',
|
||||
undefined: 'undefined',
|
||||
object: 'object',
|
||||
function: 'function'
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array or not.
|
||||
*/
|
||||
export function isArray(array: any): array is any[] {
|
||||
if (Array.isArray) {
|
||||
return Array.isArray(array);
|
||||
}
|
||||
|
||||
if (array && typeof (array.length) === _typeof.number && array.constructor === Array) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Array.isArray(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript String or not.
|
||||
*/
|
||||
export function isString(str: any): str is string {
|
||||
if (typeof (str) === _typeof.string || str instanceof String) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (typeof str === 'string');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Array and each element in the array is a string.
|
||||
*/
|
||||
export function isStringArray(value: any): value is string[] {
|
||||
return isArray(value) && (<any[]>value).every(elem => isString(elem));
|
||||
return Array.isArray(value) && (<any[]>value).every(elem => isString(elem));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +35,7 @@ export function isObject(obj: any): obj is Object {
|
|||
// The method can't do a type cast since there are type (like strings) which
|
||||
// are subclasses of any put not positvely matched by the function. Hence type
|
||||
// narrowing results in wrong results.
|
||||
return typeof obj === _typeof.object
|
||||
return typeof obj === 'object'
|
||||
&& obj !== null
|
||||
&& !Array.isArray(obj)
|
||||
&& !(obj instanceof RegExp)
|
||||
|
@ -67,32 +47,28 @@ export function isObject(obj: any): obj is Object {
|
|||
* @returns whether the provided parameter is a JavaScript Number or not.
|
||||
*/
|
||||
export function isNumber(obj: any): obj is number {
|
||||
if ((typeof (obj) === _typeof.number || obj instanceof Number) && !isNaN(obj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (typeof obj === 'number' && !isNaN(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is a JavaScript Boolean or not.
|
||||
*/
|
||||
export function isBoolean(obj: any): obj is boolean {
|
||||
return obj === true || obj === false;
|
||||
return (obj === true || obj === false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined.
|
||||
*/
|
||||
export function isUndefined(obj: any): obj is undefined {
|
||||
return typeof (obj) === _typeof.undefined;
|
||||
return (typeof obj === 'undefined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the provided parameter is undefined or null.
|
||||
*/
|
||||
export function isUndefinedOrNull(obj: any): obj is undefined | null {
|
||||
return isUndefined(obj) || obj === null;
|
||||
return (isUndefined(obj) || obj === null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -158,7 +134,7 @@ export function isEmptyObject(obj: any): obj is any {
|
|||
* @returns whether the provided parameter is a JavaScript Function or not.
|
||||
*/
|
||||
export function isFunction(obj: any): obj is Function {
|
||||
return typeof obj === _typeof.function;
|
||||
return (typeof obj === 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -237,11 +237,9 @@
|
|||
.quick-input-list .quick-input-list-entry-action-bar .action-label {
|
||||
/*
|
||||
* By default, actions in the quick input action bar are hidden
|
||||
* until hovered over them or selected. We do not use display:none
|
||||
* so that the amount of visual flickering is little by reserving the
|
||||
* space the button needs still.
|
||||
* until hovered over them or selected.
|
||||
*/
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
|
||||
|
@ -266,5 +264,5 @@
|
|||
.quick-input-list .quick-input-list-entry .quick-input-list-entry-action-bar .action-label.always-visible,
|
||||
.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar .action-label,
|
||||
.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar .action-label {
|
||||
visibility: visible;
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -386,7 +386,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||
private _matchOnLabel = true;
|
||||
private _sortByLabel = true;
|
||||
private _autoFocusOnList = true;
|
||||
private _itemActivation = ItemActivation.FIRST;
|
||||
private _itemActivation = this.ui.isScreenReaderOptimized() ? ItemActivation.NONE /* https://github.com/microsoft/vscode/issues/57501 */ : ItemActivation.FIRST;
|
||||
private _activeItems: T[] = [];
|
||||
private activeItemsUpdated = false;
|
||||
private activeItemsToConfirm: T[] | null = [];
|
||||
|
@ -637,7 +637,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||
|
||||
private trySelectFirst() {
|
||||
if (this.autoFocusOnList) {
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
if (!this.canSelectMany) {
|
||||
this.ui.list.focus(QuickInputListFocus.First);
|
||||
}
|
||||
}
|
||||
|
@ -683,22 +683,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||
event.preventDefault();
|
||||
break;
|
||||
case KeyCode.PageDown:
|
||||
if (this.ui.list.getFocusedElements().length) {
|
||||
this.ui.list.focus(QuickInputListFocus.NextPage);
|
||||
} else {
|
||||
this.ui.list.focus(QuickInputListFocus.First);
|
||||
}
|
||||
this.ui.list.focus(QuickInputListFocus.NextPage);
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.domFocus();
|
||||
}
|
||||
event.preventDefault();
|
||||
break;
|
||||
case KeyCode.PageUp:
|
||||
if (this.ui.list.getFocusedElements().length) {
|
||||
this.ui.list.focus(QuickInputListFocus.PreviousPage);
|
||||
} else {
|
||||
this.ui.list.focus(QuickInputListFocus.Last);
|
||||
}
|
||||
this.ui.list.focus(QuickInputListFocus.PreviousPage);
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.domFocus();
|
||||
}
|
||||
|
@ -875,6 +867,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
|
||||
this.ui.count.setCount(this.ui.list.getCheckedCount());
|
||||
switch (this._itemActivation) {
|
||||
case ItemActivation.NONE:
|
||||
this._itemActivation = ItemActivation.FIRST; // only valid once, then unset
|
||||
break;
|
||||
case ItemActivation.SECOND:
|
||||
this.ui.list.focus(QuickInputListFocus.Second);
|
||||
this._itemActivation = ItemActivation.FIRST; // only valid once, then unset
|
||||
|
@ -1086,30 +1081,13 @@ export class QuickInputController extends Disposable {
|
|||
}
|
||||
|
||||
private registerKeyModsListeners() {
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.Ctrl:
|
||||
case KeyCode.Meta:
|
||||
this.keyMods.ctrlCmd = true;
|
||||
break;
|
||||
case KeyCode.Alt:
|
||||
this.keyMods.alt = true;
|
||||
break;
|
||||
}
|
||||
}));
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.Ctrl:
|
||||
case KeyCode.Meta:
|
||||
this.keyMods.ctrlCmd = false;
|
||||
break;
|
||||
case KeyCode.Alt:
|
||||
this.keyMods.alt = false;
|
||||
break;
|
||||
}
|
||||
}));
|
||||
const listener = (e: KeyboardEvent | MouseEvent) => {
|
||||
this.keyMods.ctrlCmd = e.ctrlKey || e.metaKey;
|
||||
this.keyMods.alt = e.altKey;
|
||||
};
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, listener, true));
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.KEY_UP, listener, true));
|
||||
this._register(dom.addDisposableListener(window, dom.EventType.MOUSE_DOWN, listener, true));
|
||||
}
|
||||
|
||||
private getUI() {
|
||||
|
@ -1362,6 +1340,9 @@ export class QuickInputController extends Disposable {
|
|||
];
|
||||
input.canSelectMany = !!options.canPickMany;
|
||||
input.placeholder = options.placeHolder;
|
||||
if (options.placeHolder) {
|
||||
input.ariaLabel = options.placeHolder;
|
||||
}
|
||||
input.ignoreFocusOut = !!options.ignoreFocusLost;
|
||||
input.matchOnDescription = !!options.matchOnDescription;
|
||||
input.matchOnDetail = !!options.matchOnDetail;
|
||||
|
|
|
@ -302,14 +302,12 @@ export class QuickInputList {
|
|||
}
|
||||
break;
|
||||
case KeyCode.UpArrow:
|
||||
case KeyCode.PageUp:
|
||||
const focus1 = this.list.getFocus();
|
||||
if (focus1.length === 1 && focus1[0] === 0) {
|
||||
this._onLeave.fire();
|
||||
}
|
||||
break;
|
||||
case KeyCode.DownArrow:
|
||||
case KeyCode.PageDown:
|
||||
const focus2 = this.list.getFocus();
|
||||
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
|
||||
this._onLeave.fire();
|
||||
|
@ -518,11 +516,11 @@ export class QuickInputList {
|
|||
return;
|
||||
}
|
||||
|
||||
if ((what === QuickInputListFocus.Next || what === QuickInputListFocus.NextPage) && this.list.getFocus()[0] === this.list.length - 1) {
|
||||
if (what === QuickInputListFocus.Next && this.list.getFocus()[0] === this.list.length - 1) {
|
||||
what = QuickInputListFocus.First;
|
||||
}
|
||||
|
||||
if ((what === QuickInputListFocus.Previous || what === QuickInputListFocus.PreviousPage) && this.list.getFocus()[0] === 0) {
|
||||
if (what === QuickInputListFocus.Previous && this.list.getFocus()[0] === 0) {
|
||||
what = QuickInputListFocus.Last;
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,8 @@ export interface IQuickPickAcceptEvent {
|
|||
}
|
||||
|
||||
export enum ItemActivation {
|
||||
FIRST = 1,
|
||||
NONE,
|
||||
FIRST,
|
||||
SECOND,
|
||||
LAST
|
||||
}
|
||||
|
@ -326,7 +327,7 @@ export type QuickPickInput<T = IQuickPickItem> = T | IQuickPickSeparator;
|
|||
|
||||
//region Fuzzy Scorer Support
|
||||
|
||||
export type IQuickPickItemWithResource = IQuickPickItem & { resource: URI | undefined };
|
||||
export type IQuickPickItemWithResource = IQuickPickItem & { resource?: URI };
|
||||
|
||||
export class QuickPickItemScorerAccessor implements IItemAccessor<IQuickPickItemWithResource> {
|
||||
|
||||
|
|
|
@ -42,20 +42,28 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
|
|||
}
|
||||
}
|
||||
|
||||
function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score {
|
||||
return scorer.score(target, scorer.prepareQuery(query), fuzzy);
|
||||
function _doScore(target: string, query: string, fuzzy: boolean): scorer.FuzzyScore {
|
||||
const preparedQuery = scorer.prepareQuery(query);
|
||||
|
||||
return scorer.scoreFuzzy(target, preparedQuery.normalized, preparedQuery.normalizedLowercase, fuzzy);
|
||||
}
|
||||
|
||||
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache): scorer.IItemScore {
|
||||
return scorer.scoreItem(item, scorer.prepareQuery(query), fuzzy, accessor, cache);
|
||||
function _doScore2(target: string, query: string): scorer.FuzzyScore2 {
|
||||
const preparedQuery = scorer.prepareQuery(query);
|
||||
|
||||
return scorer.scoreFuzzy2(target, preparedQuery);
|
||||
}
|
||||
|
||||
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache): number {
|
||||
return scorer.compareItemsByScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache);
|
||||
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.FuzzyScorerCache): scorer.IItemScore {
|
||||
return scorer.scoreItemFuzzy(item, scorer.prepareQuery(query), fuzzy, accessor, cache);
|
||||
}
|
||||
|
||||
function compareItemsByScore<T>(itemA: T, itemB: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.FuzzyScorerCache): number {
|
||||
return scorer.compareItemsByFuzzyScore(itemA, itemB, scorer.prepareQuery(query), fuzzy, accessor, cache);
|
||||
}
|
||||
|
||||
const NullAccessor = new NullAccessorClass();
|
||||
let cache: scorer.ScorerCache = Object.create(null);
|
||||
let cache: scorer.FuzzyScorerCache = Object.create(null);
|
||||
|
||||
suite('Fuzzy Scorer', () => {
|
||||
|
||||
|
@ -66,7 +74,7 @@ suite('Fuzzy Scorer', () => {
|
|||
test('score (fuzzy)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const scores: scorer.Score[] = [];
|
||||
const scores: scorer.FuzzyScore[] = [];
|
||||
scores.push(_doScore(target, 'HelLo-World', true)); // direct case match
|
||||
scores.push(_doScore(target, 'hello-world', true)); // direct mix-case match
|
||||
scores.push(_doScore(target, 'HW', true)); // direct case prefix (multiple)
|
||||
|
@ -109,42 +117,6 @@ suite('Fuzzy Scorer', () => {
|
|||
assert.equal(_doScore(target, 'eo', false)[0], 0);
|
||||
});
|
||||
|
||||
test('score (fuzzy, multiple)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const [firstSingleScore, firstSinglePositions] = _doScore(target, 'HelLo', true);
|
||||
const [secondSingleScore, secondSinglePositions] = _doScore(target, 'World', true);
|
||||
const firstAndSecondSinglePositions = [...firstSinglePositions, ...secondSinglePositions];
|
||||
|
||||
let [multiScore, multiPositions] = _doScore(target, 'HelLo World', true);
|
||||
|
||||
function assertScore() {
|
||||
assert.ok(multiScore >= firstSingleScore + secondSingleScore);
|
||||
for (let i = 0; i < multiPositions.length; i++) {
|
||||
assert.equal(multiPositions[i], firstAndSecondSinglePositions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNoScore() {
|
||||
assert.equal(multiScore, 0);
|
||||
assert.equal(multiPositions.length, 0);
|
||||
}
|
||||
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo', true);
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo World', true);
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'World HelLo Nothing', true);
|
||||
assertNoScore();
|
||||
|
||||
[multiScore, multiPositions] = _doScore(target, 'More Nothing', true);
|
||||
assertNoScore();
|
||||
});
|
||||
|
||||
test('scoreItem - matches are proper', function () {
|
||||
let res = scoreItem(null, 'something', true, ResourceAccessor, cache);
|
||||
assert.ok(!res.score);
|
||||
|
@ -217,6 +189,49 @@ suite('Fuzzy Scorer', () => {
|
|||
assert.ok(pathRes.score > noRes.score);
|
||||
});
|
||||
|
||||
test('scoreItem - multiple', function () {
|
||||
const resource = URI.file('/xyz/some/path/someFile123.txt');
|
||||
|
||||
let res1 = scoreItem(resource, 'xyz some', true, ResourceAccessor, cache);
|
||||
assert.ok(res1.score);
|
||||
assert.equal(res1.labelMatch?.length, 1);
|
||||
assert.equal(res1.labelMatch![0].start, 0);
|
||||
assert.equal(res1.labelMatch![0].end, 4);
|
||||
assert.equal(res1.descriptionMatch?.length, 1);
|
||||
assert.equal(res1.descriptionMatch![0].start, 1);
|
||||
assert.equal(res1.descriptionMatch![0].end, 4);
|
||||
|
||||
let res2 = scoreItem(resource, 'some xyz', true, ResourceAccessor, cache);
|
||||
assert.ok(res2.score);
|
||||
assert.equal(res1.score, res2.score);
|
||||
assert.equal(res2.labelMatch?.length, 1);
|
||||
assert.equal(res2.labelMatch![0].start, 0);
|
||||
assert.equal(res2.labelMatch![0].end, 4);
|
||||
assert.equal(res2.descriptionMatch?.length, 1);
|
||||
assert.equal(res2.descriptionMatch![0].start, 1);
|
||||
assert.equal(res2.descriptionMatch![0].end, 4);
|
||||
|
||||
let res3 = scoreItem(resource, 'some xyz file file123', true, ResourceAccessor, cache);
|
||||
assert.ok(res3.score);
|
||||
assert.ok(res3.score > res2.score);
|
||||
assert.equal(res3.labelMatch?.length, 1);
|
||||
assert.equal(res3.labelMatch![0].start, 0);
|
||||
assert.equal(res3.labelMatch![0].end, 11);
|
||||
assert.equal(res3.descriptionMatch?.length, 1);
|
||||
assert.equal(res3.descriptionMatch![0].start, 1);
|
||||
assert.equal(res3.descriptionMatch![0].end, 4);
|
||||
|
||||
let res4 = scoreItem(resource, 'path z y', true, ResourceAccessor, cache);
|
||||
assert.ok(res4.score);
|
||||
assert.ok(res4.score < res2.score);
|
||||
assert.equal(res4.labelMatch?.length, 0);
|
||||
assert.equal(res4.descriptionMatch?.length, 2);
|
||||
assert.equal(res4.descriptionMatch![0].start, 2);
|
||||
assert.equal(res4.descriptionMatch![0].end, 4);
|
||||
assert.equal(res4.descriptionMatch![1].start, 10);
|
||||
assert.equal(res4.descriptionMatch![1].end, 14);
|
||||
});
|
||||
|
||||
test('scoreItem - invalid input', function () {
|
||||
|
||||
let res = scoreItem(null, null!, true, ResourceAccessor, cache);
|
||||
|
@ -878,6 +893,11 @@ suite('Fuzzy Scorer', () => {
|
|||
assert.equal(query.values?.[1].normalized, 'World');
|
||||
assert.equal(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
|
||||
|
||||
let restoredQuery = scorer.pieceToQuery(query.values!);
|
||||
assert.equal(restoredQuery.original, query.original);
|
||||
assert.equal(restoredQuery.values?.length, query.values?.length);
|
||||
assert.equal(restoredQuery.containsPathSeparator, query.containsPathSeparator);
|
||||
|
||||
// with spaces that are empty
|
||||
query = scorer.prepareQuery(' Hello World ');
|
||||
assert.equal(query.original, ' Hello World ');
|
||||
|
@ -911,4 +931,48 @@ suite('Fuzzy Scorer', () => {
|
|||
assert.equal(scorer.prepareQuery('\\some\\path').containsPathSeparator, true);
|
||||
}
|
||||
});
|
||||
|
||||
test('fuzzyScore2 (multiple queries)', function () {
|
||||
const target = 'HeLlo-World';
|
||||
|
||||
const [firstSingleScore, firstSingleMatches] = _doScore2(target, 'HelLo');
|
||||
const [secondSingleScore, secondSingleMatches] = _doScore2(target, 'World');
|
||||
const firstAndSecondSingleMatches = [...firstSingleMatches || [], ...secondSingleMatches || []];
|
||||
|
||||
let [multiScore, multiMatches] = _doScore2(target, 'HelLo World');
|
||||
|
||||
function assertScore() {
|
||||
assert.ok(multiScore ?? 0 >= ((firstSingleScore ?? 0) + (secondSingleScore ?? 0)));
|
||||
for (let i = 0; multiMatches && i < multiMatches.length; i++) {
|
||||
const multiMatch = multiMatches[i];
|
||||
const firstAndSecondSingleMatch = firstAndSecondSingleMatches[i];
|
||||
|
||||
if (multiMatch && firstAndSecondSingleMatch) {
|
||||
assert.equal(multiMatch.start, firstAndSecondSingleMatch.start);
|
||||
assert.equal(multiMatch.end, firstAndSecondSingleMatch.end);
|
||||
} else {
|
||||
assert.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertNoScore() {
|
||||
assert.equal(multiScore, 0);
|
||||
assert.equal(multiMatches.length, 0);
|
||||
}
|
||||
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiMatches] = _doScore2(target, 'World HelLo');
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiMatches] = _doScore2(target, 'World HelLo World');
|
||||
assertScore();
|
||||
|
||||
[multiScore, multiMatches] = _doScore2(target, 'World HelLo Nothing');
|
||||
assertNoScore();
|
||||
|
||||
[multiScore, multiMatches] = _doScore2(target, 'More Nothing');
|
||||
assertNoScore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -704,6 +704,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
|
|||
*/
|
||||
getVisibleRanges(): Range[];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getVisibleRangesPlusViewportAboveBelow(): Range[];
|
||||
|
||||
/**
|
||||
* Get the view zones.
|
||||
* @internal
|
||||
|
|
|
@ -460,6 +460,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
|
|||
return this._modelData.viewModel.getVisibleRanges();
|
||||
}
|
||||
|
||||
public getVisibleRangesPlusViewportAboveBelow(): Range[] {
|
||||
if (!this._modelData) {
|
||||
return [];
|
||||
}
|
||||
return this._modelData.viewModel.getVisibleRangesPlusViewportAboveBelow();
|
||||
}
|
||||
|
||||
public getWhitespaces(): IEditorWhitespace[] {
|
||||
if (!this._modelData) {
|
||||
return [];
|
||||
|
|
|
@ -890,15 +890,18 @@ export class TokensStore2 {
|
|||
}
|
||||
|
||||
public setPartial(_range: Range, pieces: MultilineTokens2[]): Range {
|
||||
if (pieces.length === 0) {
|
||||
return _range;
|
||||
// console.log(`setPartial ${_range} ${pieces.map(p => p.toString()).join(', ')}`);
|
||||
|
||||
let range = _range;
|
||||
if (pieces.length > 0) {
|
||||
const _firstRange = pieces[0].getRange();
|
||||
const _lastRange = pieces[pieces.length - 1].getRange();
|
||||
if (!_firstRange || !_lastRange) {
|
||||
return _range;
|
||||
}
|
||||
range = _range.plusRange(_firstRange).plusRange(_lastRange);
|
||||
}
|
||||
const _firstRange = pieces[0].getRange();
|
||||
const _lastRange = pieces[pieces.length - 1].getRange();
|
||||
if (!_firstRange || !_lastRange) {
|
||||
return _range;
|
||||
}
|
||||
const range = _range.plusRange(_firstRange).plusRange(_lastRange);
|
||||
|
||||
let insertPosition: { index: number; } | null = null;
|
||||
for (let i = 0, len = this._pieces.length; i < len; i++) {
|
||||
const piece = this._pieces[i];
|
||||
|
@ -938,6 +941,15 @@ export class TokensStore2 {
|
|||
|
||||
// after removal, this piece contains the range
|
||||
const [a, b] = piece.split(range);
|
||||
if (a.isEmpty()) {
|
||||
// this piece is actually after the range
|
||||
insertPosition = insertPosition || { index: i };
|
||||
continue;
|
||||
}
|
||||
if (b.isEmpty()) {
|
||||
// this piece is actually before the range
|
||||
continue;
|
||||
}
|
||||
this._pieces.splice(i, 1, a, b);
|
||||
i++;
|
||||
len++;
|
||||
|
@ -947,10 +959,12 @@ export class TokensStore2 {
|
|||
|
||||
insertPosition = insertPosition || { index: this._pieces.length };
|
||||
|
||||
this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces);
|
||||
if (pieces.length > 0) {
|
||||
this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces);
|
||||
}
|
||||
|
||||
// console.log(`I HAVE ${this._pieces.length} pieces`);
|
||||
// console.log(`${this._pieces.map(p => p.toString()).join(', ')}`);
|
||||
// console.log(`${this._pieces.map(p => p.toString()).join('\n')}`);
|
||||
|
||||
return range;
|
||||
}
|
||||
|
|
|
@ -473,7 +473,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
|||
const model = modelData.model;
|
||||
let maintainUndoRedoStack = false;
|
||||
let heapSize = 0;
|
||||
if (MAINTAIN_UNDO_REDO_STACK && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote)) {
|
||||
if (MAINTAIN_UNDO_REDO_STACK && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData)) {
|
||||
const elements = this._undoRedoService.getElements(resource);
|
||||
if ((elements.past.length > 0 || elements.future.length > 0) && isEditStackPastFutureElements(elements)) {
|
||||
maintainUndoRedoStack = true;
|
||||
|
@ -577,9 +577,17 @@ export interface ILineSequence {
|
|||
getLineContent(lineNumber: number): string;
|
||||
}
|
||||
|
||||
class SemanticColoringFeature extends Disposable {
|
||||
export const SEMANTIC_HIGHLIGHTING_SETTING_ID = 'editor.semanticHighlighting';
|
||||
|
||||
private static readonly SETTING_ID = 'editor.semanticHighlighting';
|
||||
export function isSemanticColoringEnabled(model: ITextModel, themeService: IThemeService, configurationService: IConfigurationService): boolean {
|
||||
if (!themeService.getColorTheme().semanticHighlighting) {
|
||||
return false;
|
||||
}
|
||||
const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SEMANTIC_HIGHLIGHTING_SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
|
||||
return Boolean(options && options.enabled);
|
||||
}
|
||||
|
||||
class SemanticColoringFeature extends Disposable {
|
||||
|
||||
private readonly _watchers: Record<string, ModelSemanticColoring>;
|
||||
private readonly _semanticStyling: SemanticStyling;
|
||||
|
@ -589,13 +597,6 @@ class SemanticColoringFeature extends Disposable {
|
|||
this._watchers = Object.create(null);
|
||||
this._semanticStyling = semanticStyling;
|
||||
|
||||
const isSemanticColoringEnabled = (model: ITextModel) => {
|
||||
if (!themeService.getColorTheme().semanticHighlighting) {
|
||||
return false;
|
||||
}
|
||||
const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
|
||||
return options && options.enabled;
|
||||
};
|
||||
const register = (model: ITextModel) => {
|
||||
this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
|
||||
};
|
||||
|
@ -606,7 +607,7 @@ class SemanticColoringFeature extends Disposable {
|
|||
const handleSettingOrThemeChange = () => {
|
||||
for (let model of modelService.getModels()) {
|
||||
const curr = this._watchers[model.uri.toString()];
|
||||
if (isSemanticColoringEnabled(model)) {
|
||||
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
|
||||
if (!curr) {
|
||||
register(model);
|
||||
}
|
||||
|
@ -618,7 +619,7 @@ class SemanticColoringFeature extends Disposable {
|
|||
}
|
||||
};
|
||||
this._register(modelService.onModelAdded((model) => {
|
||||
if (isSemanticColoringEnabled(model)) {
|
||||
if (isSemanticColoringEnabled(model, themeService, configurationService)) {
|
||||
register(model);
|
||||
}
|
||||
}));
|
||||
|
@ -629,7 +630,7 @@ class SemanticColoringFeature extends Disposable {
|
|||
}
|
||||
}));
|
||||
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
|
||||
if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
|
||||
handleSettingOrThemeChange();
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -402,8 +402,26 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
|||
this._updateConfigurationViewLineCount.schedule();
|
||||
}
|
||||
|
||||
public getVisibleRangesPlusViewportAboveBelow(): Range[] {
|
||||
const layoutInfo = this.configuration.options.get(EditorOption.layoutInfo);
|
||||
const lineHeight = this.configuration.options.get(EditorOption.lineHeight);
|
||||
const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight));
|
||||
const partialData = this.viewLayout.getLinesViewportData();
|
||||
const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround);
|
||||
const endViewLineNumber = Math.min(this.getLineCount(), partialData.completelyVisibleEndLineNumber + linesAround);
|
||||
|
||||
return this._toModelVisibleRanges(new Range(
|
||||
startViewLineNumber, this.getLineMinColumn(startViewLineNumber),
|
||||
endViewLineNumber, this.getLineMaxColumn(endViewLineNumber)
|
||||
));
|
||||
}
|
||||
|
||||
public getVisibleRanges(): Range[] {
|
||||
const visibleViewRange = this.getCompletelyVisibleViewRange();
|
||||
return this._toModelVisibleRanges(visibleViewRange);
|
||||
}
|
||||
|
||||
private _toModelVisibleRanges(visibleViewRange: Range): Range[] {
|
||||
const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
|
||||
const hiddenAreas = this.lines.getHiddenAreas();
|
||||
|
||||
|
|
|
@ -924,7 +924,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
|||
return null;
|
||||
}
|
||||
try {
|
||||
new RegExp(value);
|
||||
// use `g` and `u` which are also used by the TextModel search
|
||||
new RegExp(value, 'gu');
|
||||
return null;
|
||||
} catch (e) {
|
||||
return { content: e.message };
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.14646 9.76783L8.14644 14.7678L8.85355 14.7678L13.8535 9.76783L13.1464 9.06072L9 13.2072L9 1.00006L8 1.00006L8 13.2072L3.85356 9.06072L3.14646 9.76783Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 324 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.14646 9.76783L8.14644 14.7678L8.85355 14.7678L13.8535 9.76783L13.1464 9.06072L9 13.2072L9 1.00006L8 1.00006L8 13.2072L3.85356 9.06072L3.14646 9.76783Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 324 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8535 6.29284L8.85356 1.29285H8.14645L3.14645 6.29284L3.85356 6.99995L8 2.85351V15.0606H9V2.85351L13.1464 6.99995L13.8535 6.29284Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 304 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8535 6.29284L8.85356 1.29285H8.14645L3.14645 6.29284L3.85356 6.99995L8 2.85351V15.0606H9V2.85351L13.1464 6.99995L13.8535 6.29284Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 304 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 8.70714L11.6464 12.3536L12.3536 11.6465L8.70711 8.00004L12.3536 4.35359L11.6464 3.64648L8 7.29293L4.35355 3.64648L3.64645 4.35359L7.29289 8.00004L3.64645 11.6465L4.35355 12.3536L8 8.70714Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 362 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 8.70714L11.6464 12.3536L12.3536 11.6465L8.70711 8.00004L12.3536 4.35359L11.6464 3.64648L8 7.29293L4.35355 3.64648L3.64645 4.35359L7.29289 8.00004L3.64645 11.6465L4.35355 12.3536L8 8.70714Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 362 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 12L1 11H10V12H1ZM1 7H15V8H1L1 7ZM12 3V4H1L1 3H12Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 183 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 12L1 11H10V12H1ZM1 7H15V8H1L1 7ZM12 3V4H1L1 3H12Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 183 B |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6009 2.67683C11.7474 2.36708 11.9559 2.2122 12.2263 2.2122C12.4742 2.2122 12.6651 2.32987 12.7991 2.56522C12.933 2.80056 13 3.12243 13 3.53082C13 3.97383 12.9218 4.32944 12.7653 4.59766C12.6088 4.86589 12.3997 5 12.138 5C11.9014 5 11.7224 4.87541 11.6009 4.62622H11.5934V4.93511H11V1H11.5934V2.67683H11.6009ZM11.584 3.77742C11.584 3.94873 11.6197 4.09063 11.6911 4.20311C11.7624 4.3156 11.8538 4.37184 11.9653 4.37184C12.1005 4.37184 12.205 4.30002 12.2789 4.15639C12.354 4.01103 12.3915 3.80597 12.3915 3.54121C12.3915 3.32144 12.3571 3.15012 12.2883 3.02726C12.2207 2.90266 12.1236 2.84036 11.9972 2.84036C11.8782 2.84036 11.7793 2.9018 11.7005 3.02466C11.6228 3.14752 11.584 3.30759 11.584 3.50487V3.77742ZM4.11969 7.695L2 5.56781L2.66188 4.90594L3.66781 5.90625V4.39594C3.66695 4.21309 3.70219 4.03187 3.7715 3.86266C3.84082 3.69346 3.94286 3.53961 4.07176 3.40992C4.20066 3.28023 4.3539 3.17727 4.52268 3.10692C4.69146 3.03658 4.87246 3.00024 5.05531 3H7.39906V3.90469H5.05531C4.92856 3.91026 4.8089 3.96476 4.72149 4.05672C4.63408 4.14868 4.58571 4.27094 4.58656 4.39781L4.59406 5.89781L5.54281 4.95375L6.19906 5.61L4.11969 7.695ZM9.3556 4.93017H10V3.22067C10 2.40689 9.68534 2 9.05603 2C8.92098 2 8.77083 2.02421 8.6056 2.07263C8.44181 2.12104 8.3125 2.17691 8.21767 2.24022V2.90503C8.45474 2.70205 8.70474 2.60056 8.96767 2.60056C9.22917 2.60056 9.35991 2.75698 9.35991 3.06983L8.76078 3.17318C8.25359 3.25885 8 3.57914 8 4.13408C8 4.39665 8.06106 4.60708 8.18319 4.76536C8.30675 4.92179 8.47557 5 8.68966 5C8.97989 5 9.19899 4.83985 9.34698 4.51955H9.3556V4.93017ZM9.35991 3.57542V3.76816C9.35991 3.9432 9.31968 4.08845 9.23922 4.20391C9.15876 4.3175 9.0546 4.3743 8.92672 4.3743C8.83477 4.3743 8.76149 4.34264 8.7069 4.27933C8.65374 4.21415 8.62716 4.13128 8.62716 4.03073C8.62716 3.80912 8.73779 3.6797 8.95905 3.64246L9.35991 3.57542ZM7 12.9302H6.3556V12.5196H6.34698C6.19899 12.8399 5.97989 13 5.68966 13C5.47557 13 5.30675 12.9218 5.18319 12.7654C5.06106 12.6071 5 12.3966 5 12.1341C5 11.5791 5.25359 11.2588 5.76078 11.1732L6.35991 11.0698C6.35991 10.757 6.22917 10.6006 5.96767 10.6006C5.70474 10.6006 5.45474 10.702 5.21767 10.905V10.2402C5.3125 10.1769 5.44181 10.121 5.6056 10.0726C5.77083 10.0242 5.92098 10 6.05603 10C6.68534 10 7 10.4069 7 11.2207V12.9302ZM6.35991 11.7682V11.5754L5.95905 11.6425C5.73779 11.6797 5.62716 11.8091 5.62716 12.0307C5.62716 12.1313 5.65374 12.2142 5.7069 12.2793C5.76149 12.3426 5.83477 12.3743 5.92672 12.3743C6.0546 12.3743 6.15876 12.3175 6.23922 12.2039C6.31968 12.0885 6.35991 11.9432 6.35991 11.7682ZM9.26165 13C9.58343 13 9.82955 12.9423 10 12.8268V12.1173C9.81999 12.2551 9.636 12.324 9.44803 12.324C9.23616 12.324 9.06969 12.2523 8.94863 12.1089C8.82756 11.9637 8.76702 11.7644 8.76702 11.5112C8.76702 11.2505 8.82995 11.0466 8.95579 10.8994C9.08323 10.7505 9.25528 10.676 9.47192 10.676C9.66627 10.676 9.84229 10.7449 10 10.8827V10.1341C9.87097 10.0447 9.66229 10 9.37395 10C8.95659 10 8.62286 10.1406 8.37276 10.4218C8.12425 10.7011 8 11.0838 8 11.5698C8 11.9907 8.11629 12.3343 8.34887 12.6006C8.58144 12.8669 8.8857 13 9.26165 13ZM2 9L3 8H12L13 9V14L12 15H3L2 14V9ZM3 9V14H12V9H3ZM6 7L7 6H14L15 7V12L14 13V12V7H7H6Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6009 2.67683C11.7474 2.36708 11.9559 2.2122 12.2263 2.2122C12.4742 2.2122 12.6651 2.32987 12.7991 2.56522C12.933 2.80056 13 3.12243 13 3.53082C13 3.97383 12.9218 4.32944 12.7653 4.59766C12.6088 4.86589 12.3997 5 12.138 5C11.9014 5 11.7224 4.87541 11.6009 4.62622H11.5934V4.93511H11V1H11.5934V2.67683H11.6009ZM11.584 3.77742C11.584 3.94873 11.6197 4.09063 11.6911 4.20311C11.7624 4.3156 11.8538 4.37184 11.9653 4.37184C12.1005 4.37184 12.205 4.30002 12.2789 4.15639C12.354 4.01103 12.3915 3.80597 12.3915 3.54121C12.3915 3.32144 12.3571 3.15012 12.2883 3.02726C12.2207 2.90266 12.1236 2.84036 11.9972 2.84036C11.8782 2.84036 11.7793 2.9018 11.7005 3.02466C11.6228 3.14752 11.584 3.30759 11.584 3.50487V3.77742ZM4.11969 7.695L2 5.56781L2.66188 4.90594L3.66781 5.90625V4.39594C3.66695 4.21309 3.70219 4.03187 3.7715 3.86266C3.84082 3.69346 3.94286 3.53961 4.07176 3.40992C4.20066 3.28023 4.3539 3.17727 4.52268 3.10692C4.69146 3.03658 4.87246 3.00024 5.05531 3H7.39906V3.90469H5.05531C4.92856 3.91026 4.8089 3.96476 4.72149 4.05672C4.63408 4.14868 4.58571 4.27094 4.58656 4.39781L4.59406 5.89781L5.54281 4.95375L6.19906 5.61L4.11969 7.695ZM9.3556 4.93017H10V3.22067C10 2.40689 9.68534 2 9.05603 2C8.92098 2 8.77083 2.02421 8.6056 2.07263C8.44181 2.12104 8.3125 2.17691 8.21767 2.24022V2.90503C8.45474 2.70205 8.70474 2.60056 8.96767 2.60056C9.22917 2.60056 9.35991 2.75698 9.35991 3.06983L8.76078 3.17318C8.25359 3.25885 8 3.57914 8 4.13408C8 4.39665 8.06106 4.60708 8.18319 4.76536C8.30675 4.92179 8.47557 5 8.68966 5C8.97989 5 9.19899 4.83985 9.34698 4.51955H9.3556V4.93017ZM9.35991 3.57542V3.76816C9.35991 3.9432 9.31968 4.08845 9.23922 4.20391C9.15876 4.3175 9.0546 4.3743 8.92672 4.3743C8.83477 4.3743 8.76149 4.34264 8.7069 4.27933C8.65374 4.21415 8.62716 4.13128 8.62716 4.03073C8.62716 3.80912 8.73779 3.6797 8.95905 3.64246L9.35991 3.57542ZM7 12.9302H6.3556V12.5196H6.34698C6.19899 12.8399 5.97989 13 5.68966 13C5.47557 13 5.30675 12.9218 5.18319 12.7654C5.06106 12.6071 5 12.3966 5 12.1341C5 11.5791 5.25359 11.2588 5.76078 11.1732L6.35991 11.0698C6.35991 10.757 6.22917 10.6006 5.96767 10.6006C5.70474 10.6006 5.45474 10.702 5.21767 10.905V10.2402C5.3125 10.1769 5.44181 10.121 5.6056 10.0726C5.77083 10.0242 5.92098 10 6.05603 10C6.68534 10 7 10.4069 7 11.2207V12.9302ZM6.35991 11.7682V11.5754L5.95905 11.6425C5.73779 11.6797 5.62716 11.8091 5.62716 12.0307C5.62716 12.1313 5.65374 12.2142 5.7069 12.2793C5.76149 12.3426 5.83477 12.3743 5.92672 12.3743C6.0546 12.3743 6.15876 12.3175 6.23922 12.2039C6.31968 12.0885 6.35991 11.9432 6.35991 11.7682ZM9.26165 13C9.58343 13 9.82955 12.9423 10 12.8268V12.1173C9.81999 12.2551 9.636 12.324 9.44803 12.324C9.23616 12.324 9.06969 12.2523 8.94863 12.1089C8.82756 11.9637 8.76702 11.7644 8.76702 11.5112C8.76702 11.2505 8.82995 11.0466 8.95579 10.8994C9.08323 10.7505 9.25528 10.676 9.47192 10.676C9.66627 10.676 9.84229 10.7449 10 10.8827V10.1341C9.87097 10.0447 9.66229 10 9.37395 10C8.95659 10 8.62286 10.1406 8.37276 10.4218C8.12425 10.7011 8 11.0838 8 11.5698C8 11.9907 8.11629 12.3343 8.34887 12.6006C8.58144 12.8669 8.8857 13 9.26165 13ZM2 9L3 8H12L13 9V14L12 15H3L2 14V9ZM3 9V14H12V9H3ZM6 7L7 6H14L15 7V12L14 13V12V7H7H6Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.221 3.739L5.482 6.008L7.7 3.784L7 3.084L5.988 4.091L5.98 2.491C5.97909 2.35567 6.03068 2.22525 6.12392 2.12716C6.21716 2.02908 6.3448 1.97095 6.48 1.965H8V1H6.48C6.28496 1.00026 6.09189 1.03902 5.91186 1.11405C5.73183 1.18908 5.56838 1.29892 5.43088 1.43725C5.29338 1.57558 5.18455 1.73969 5.11061 1.92018C5.03667 2.10066 4.99908 2.29396 5 2.489V4.1L3.927 3.033L3.221 3.739ZM9.89014 5.53277H9.90141C10.0836 5.84426 10.3521 6 10.707 6C11.0995 6 11.4131 5.83236 11.6479 5.49708C11.8826 5.1618 12 4.71728 12 4.16353C12 3.65304 11.8995 3.2507 11.6986 2.95652C11.4977 2.66234 11.2113 2.51525 10.8394 2.51525C10.4338 2.51525 10.1211 2.70885 9.90141 3.09604H9.89014V1H9V5.91888H9.89014V5.53277ZM9.87606 4.47177V4.13108C9.87606 3.88449 9.93427 3.6844 10.0507 3.53082C10.169 3.37724 10.3174 3.30045 10.4958 3.30045C10.6854 3.30045 10.831 3.37833 10.9324 3.53407C11.0357 3.68765 11.0873 3.9018 11.0873 4.17651C11.0873 4.50746 11.031 4.76379 10.9183 4.94549C10.8075 5.12503 10.6507 5.2148 10.4479 5.2148C10.2808 5.2148 10.1437 5.14449 10.0366 5.00389C9.92958 4.86329 9.87606 4.68592 9.87606 4.47177ZM9 12.7691C8.74433 12.923 8.37515 13 7.89247 13C7.32855 13 6.87216 12.8225 6.5233 12.4674C6.17443 12.1124 6 11.6543 6 11.0931C6 10.4451 6.18638 9.93484 6.55914 9.5624C6.93429 9.18747 7.43489 9.00001 8.06093 9.00001C8.49343 9.00001 8.80645 9.0596 9 9.17878V10.1769C8.76344 9.99319 8.4994 9.90132 8.20789 9.90132C7.88292 9.90132 7.62485 10.0006 7.43369 10.1993C7.24492 10.3954 7.15054 10.6673 7.15054 11.0149C7.15054 11.3526 7.24134 11.6183 7.42294 11.8119C7.60454 12.0031 7.85424 12.0987 8.17204 12.0987C8.454 12.0987 8.72999 12.0068 9 11.8231V12.7691ZM4 7L3 8V14L4 15H11L12 14V8L11 7H4ZM4 8H5H10H11V9V13V14H10H5H4V13V9V8Z" fill="#C5C5C5"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.8 KiB |
|
@ -1,3 +0,0 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.221 3.739L5.482 6.008L7.7 3.784L7 3.084L5.988 4.091L5.98 2.491C5.97909 2.35567 6.03068 2.22525 6.12392 2.12716C6.21716 2.02908 6.3448 1.97095 6.48 1.965H8V1H6.48C6.28496 1.00026 6.09189 1.03902 5.91186 1.11405C5.73183 1.18908 5.56838 1.29892 5.43088 1.43725C5.29338 1.57558 5.18455 1.73969 5.11061 1.92018C5.03667 2.10066 4.99908 2.29396 5 2.489V4.1L3.927 3.033L3.221 3.739ZM9.89014 5.53277H9.90141C10.0836 5.84426 10.3521 6 10.707 6C11.0995 6 11.4131 5.83236 11.6479 5.49708C11.8826 5.1618 12 4.71728 12 4.16353C12 3.65304 11.8995 3.2507 11.6986 2.95652C11.4977 2.66234 11.2113 2.51525 10.8394 2.51525C10.4338 2.51525 10.1211 2.70885 9.90141 3.09604H9.89014V1H9V5.91888H9.89014V5.53277ZM9.87606 4.47177V4.13108C9.87606 3.88449 9.93427 3.6844 10.0507 3.53082C10.169 3.37724 10.3174 3.30045 10.4958 3.30045C10.6854 3.30045 10.831 3.37833 10.9324 3.53407C11.0357 3.68765 11.0873 3.9018 11.0873 4.17651C11.0873 4.50746 11.031 4.76379 10.9183 4.94549C10.8075 5.12503 10.6507 5.2148 10.4479 5.2148C10.2808 5.2148 10.1437 5.14449 10.0366 5.00389C9.92958 4.86329 9.87606 4.68592 9.87606 4.47177ZM9 12.7691C8.74432 12.923 8.37515 13 7.89247 13C7.32855 13 6.87216 12.8225 6.5233 12.4674C6.17443 12.1124 6 11.6543 6 11.0931C6 10.4451 6.18638 9.93484 6.55914 9.5624C6.93429 9.18747 7.43489 9.00001 8.06093 9.00001C8.49343 9.00001 8.80645 9.0596 9 9.17878V10.1769C8.76344 9.99319 8.4994 9.90132 8.20789 9.90132C7.88292 9.90132 7.62485 10.0006 7.43369 10.1993C7.24492 10.3954 7.15054 10.6673 7.15054 11.0149C7.15054 11.3526 7.24134 11.6183 7.42294 11.8119C7.60454 12.0031 7.85424 12.0987 8.17204 12.0987C8.454 12.0987 8.72999 12.0068 9 11.8231V12.7691ZM4 7L3 8V14L4 15H11L12 14V8L11 7H4ZM4 8H5H10H11V9V13V14H10H5H4V13V9V8Z" fill="#424242"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.8 KiB |
|
@ -563,11 +563,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||
const disposables = new DisposableStore();
|
||||
const actionsElement = dom.append(hoverElement, $('div.actions'));
|
||||
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
|
||||
const peekProblemLabel = nls.localize('peek problem', "Peek Problem");
|
||||
const peekProblemKeybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID);
|
||||
const peekProblemKeybindingLabel = peekProblemKeybinding && peekProblemKeybinding.getLabel();
|
||||
disposables.add(this.renderAction(actionsElement, {
|
||||
label: peekProblemKeybindingLabel ? nls.localize('titleAndKb', "{0} ({1})", peekProblemLabel, peekProblemKeybindingLabel) : peekProblemLabel,
|
||||
label: nls.localize('peek problem', "Peek Problem"),
|
||||
commandId: NextMarkerAction.ID,
|
||||
run: () => {
|
||||
this.hide();
|
||||
|
@ -604,12 +601,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||
}
|
||||
}));
|
||||
|
||||
const quickFixLabel = nls.localize('quick fixes', "Quick Fix...");
|
||||
const quickFixKeybinding = this._keybindingService.lookupKeybinding(QuickFixAction.Id);
|
||||
const quickFixKeybindingLabel = quickFixKeybinding && quickFixKeybinding.getLabel();
|
||||
|
||||
disposables.add(this.renderAction(actionsElement, {
|
||||
label: quickFixKeybindingLabel ? nls.localize('titleAndKb', "{0} ({1})", quickFixLabel, quickFixKeybindingLabel) : quickFixLabel,
|
||||
label: nls.localize('quick fixes', "Quick Fix..."),
|
||||
commandId: QuickFixAction.Id,
|
||||
run: (target) => {
|
||||
showing = true;
|
||||
|
@ -645,11 +638,9 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||
dom.append(action, $(`span.icon.${actionOptions.iconClass}`));
|
||||
}
|
||||
const label = dom.append(action, $('span'));
|
||||
label.textContent = actionOptions.label;
|
||||
const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId);
|
||||
if (keybinding) {
|
||||
label.title = `${actionOptions.label} (${keybinding.getLabel()})`;
|
||||
}
|
||||
const keybindingLabel = keybinding ? keybinding.getLabel() : null;
|
||||
label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label;
|
||||
return dom.addDisposableListener(actionContainer, dom.EventType.CLICK, e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
|
|
@ -52,6 +52,11 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu
|
|||
|
||||
// Re-create whenever the active editor changes
|
||||
disposables.add(this.onDidActiveTextEditorControlChange(() => {
|
||||
|
||||
// Clear old
|
||||
pickerDisposable.value = undefined;
|
||||
|
||||
// Add new
|
||||
pickerDisposable.value = this.doProvide(picker, token);
|
||||
}));
|
||||
|
||||
|
|
|
@ -15,14 +15,13 @@ import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry,
|
|||
import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { trim, format } from 'vs/base/common/strings';
|
||||
import { fuzzyScore, FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { prepareQuery, IPreparedQuery } from 'vs/base/common/fuzzyScorer';
|
||||
import { prepareQuery, IPreparedQuery, pieceToQuery, scoreFuzzy2 } from 'vs/base/common/fuzzyScorer';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
|
||||
export interface IGotoSymbolQuickPickItem extends IQuickPickItem {
|
||||
kind: SymbolKind,
|
||||
index: number,
|
||||
score?: FuzzyScore;
|
||||
score?: number;
|
||||
range?: { decoration: IRange, selection: IRange }
|
||||
}
|
||||
|
||||
|
@ -37,7 +36,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||
static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
|
||||
|
||||
constructor(protected options?: IGotoSymbolQuickAccessProviderOptions) {
|
||||
super(assign(options, { canAcceptInBackground: true }));
|
||||
super({ ...options, canAcceptInBackground: true });
|
||||
}
|
||||
|
||||
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
||||
|
@ -204,12 +203,12 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||
const filterBySymbolKind = query.original.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
|
||||
const filterPos = filterBySymbolKind ? 1 : 0;
|
||||
|
||||
// Split between symbol and container query if separated by space
|
||||
// Split between symbol and container query
|
||||
let symbolQuery: IPreparedQuery;
|
||||
let containerQuery: IPreparedQuery | undefined;
|
||||
if (query.values && query.values.length > 1) {
|
||||
symbolQuery = prepareQuery(query.values[0].original);
|
||||
containerQuery = prepareQuery(query.values[1].original);
|
||||
symbolQuery = pieceToQuery(query.values[0]); // symbol: only match on first part
|
||||
containerQuery = pieceToQuery(query.values.slice(1)); // container: match on all but first parts
|
||||
} else {
|
||||
symbolQuery = query;
|
||||
}
|
||||
|
@ -220,69 +219,79 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||
const symbol = symbols[index];
|
||||
|
||||
const symbolLabel = trim(symbol.name);
|
||||
const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`;
|
||||
|
||||
let containerLabel = symbol.containerName;
|
||||
if (containerLabel && options?.extraContainerLabel) {
|
||||
containerLabel = `${options.extraContainerLabel} • ${containerLabel}`;
|
||||
} else {
|
||||
containerLabel = options?.extraContainerLabel;
|
||||
}
|
||||
|
||||
let symbolScore: FuzzyScore | undefined = undefined;
|
||||
let containerScore: FuzzyScore | undefined = undefined;
|
||||
|
||||
let includeSymbol = true;
|
||||
if (query.original.length > filterPos) {
|
||||
|
||||
// Score by symbol
|
||||
symbolScore = fuzzyScore(symbolQuery.original, symbolQuery.originalLowercase, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
|
||||
includeSymbol = !!symbolScore;
|
||||
|
||||
// Score by container if specified
|
||||
if (includeSymbol && containerQuery) {
|
||||
if (containerLabel) {
|
||||
containerScore = fuzzyScore(containerQuery.original, containerQuery.originalLowercase, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
|
||||
}
|
||||
|
||||
includeSymbol = !!containerScore;
|
||||
if (options?.extraContainerLabel) {
|
||||
if (containerLabel) {
|
||||
containerLabel = `${options.extraContainerLabel} • ${containerLabel}`;
|
||||
} else {
|
||||
containerLabel = options.extraContainerLabel;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeSymbol) {
|
||||
const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`;
|
||||
const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0;
|
||||
let symbolScore: number | undefined = undefined;
|
||||
let symbolMatches: IMatch[] | undefined = undefined;
|
||||
|
||||
filteredSymbolPicks.push({
|
||||
index,
|
||||
kind: symbol.kind,
|
||||
score: symbolScore,
|
||||
label: symbolLabelWithIcon,
|
||||
ariaLabel: localize('symbolsAriaLabel', "{0}, symbols picker", symbolLabel),
|
||||
description: containerLabel,
|
||||
highlights: deprecated ? undefined : {
|
||||
label: createMatches(symbolScore, symbolLabelWithIcon.length - symbolLabel.length /* Readjust matches to account for codicons in label */),
|
||||
description: createMatches(containerScore)
|
||||
},
|
||||
range: {
|
||||
selection: Range.collapseToStart(symbol.selectionRange),
|
||||
decoration: symbol.range
|
||||
},
|
||||
strikethrough: deprecated,
|
||||
buttons: (() => {
|
||||
const openSideBySideDirection = this.options?.openSideBySideDirection();
|
||||
if (!openSideBySideDirection) {
|
||||
return undefined;
|
||||
}
|
||||
let containerScore: number | undefined = undefined;
|
||||
let containerMatches: IMatch[] | undefined = undefined;
|
||||
|
||||
return [
|
||||
{
|
||||
iconClass: openSideBySideDirection === 'right' ? 'codicon-split-horizontal' : 'codicon-split-vertical',
|
||||
tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom")
|
||||
}
|
||||
];
|
||||
})()
|
||||
});
|
||||
if (query.original.length > filterPos) {
|
||||
|
||||
// Score by symbol
|
||||
[symbolScore, symbolMatches] = scoreFuzzy2(symbolLabel, symbolQuery, filterPos, symbolLabelWithIcon.length - symbolLabel.length /* Readjust matches to account for codicons in label */);
|
||||
if (!symbolScore) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Score by container if specified
|
||||
if (containerQuery) {
|
||||
if (containerLabel && containerQuery.original.length > 0) {
|
||||
[containerScore, containerMatches] = scoreFuzzy2(containerLabel, containerQuery);
|
||||
}
|
||||
|
||||
if (!containerScore) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbolScore) {
|
||||
symbolScore += containerScore; // boost symbolScore by containerScore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0;
|
||||
|
||||
filteredSymbolPicks.push({
|
||||
index,
|
||||
kind: symbol.kind,
|
||||
score: symbolScore,
|
||||
label: symbolLabelWithIcon,
|
||||
ariaLabel: symbolLabel,
|
||||
description: containerLabel,
|
||||
highlights: deprecated ? undefined : {
|
||||
label: symbolMatches,
|
||||
description: containerMatches
|
||||
},
|
||||
range: {
|
||||
selection: Range.collapseToStart(symbol.selectionRange),
|
||||
decoration: symbol.range
|
||||
},
|
||||
strikethrough: deprecated,
|
||||
buttons: (() => {
|
||||
const openSideBySideDirection = this.options?.openSideBySideDirection();
|
||||
if (!openSideBySideDirection) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
iconClass: openSideBySideDirection === 'right' ? 'codicon-split-horizontal' : 'codicon-split-vertical',
|
||||
tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom")
|
||||
}
|
||||
];
|
||||
})()
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by score
|
||||
|
@ -351,9 +360,9 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||
}
|
||||
|
||||
if (symbolA.score && symbolB.score) {
|
||||
if (symbolA.score[0] > symbolB.score[0]) {
|
||||
if (symbolA.score > symbolB.score) {
|
||||
return -1;
|
||||
} else if (symbolA.score[0] < symbolB.score[0]) {
|
||||
} else if (symbolA.score < symbolB.score) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { first } from 'vs/base/common/async';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { onUnexpectedExternalError, canceled, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
@ -98,7 +97,7 @@ export class CompletionItem {
|
|||
this.resolve = (token) => {
|
||||
if (!cached) {
|
||||
cached = Promise.resolve(resolveCompletionItem.call(provider, model, Position.lift(position), completion, token)).then(value => {
|
||||
assign(completion, value);
|
||||
Object.assign(completion, value);
|
||||
this.isResolved = true;
|
||||
}, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
|
|
|
@ -734,6 +734,7 @@ registerEditorCommand(new SuggestCommand({
|
|||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'insertBestCompletion',
|
||||
precondition: ContextKeyExpr.and(
|
||||
EditorContextKeys.textInputFocus,
|
||||
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
|
||||
WordContextKey.AtEnd,
|
||||
SuggestContext.Visible.toNegated(),
|
||||
|
@ -753,6 +754,7 @@ registerEditorCommand(new SuggestCommand({
|
|||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'insertNextSuggestion',
|
||||
precondition: ContextKeyExpr.and(
|
||||
EditorContextKeys.textInputFocus,
|
||||
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
|
||||
SuggestAlternatives.OtherSuggestions,
|
||||
SuggestContext.Visible.toNegated(),
|
||||
|
@ -769,6 +771,7 @@ registerEditorCommand(new SuggestCommand({
|
|||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'insertPrevSuggestion',
|
||||
precondition: ContextKeyExpr.and(
|
||||
EditorContextKeys.textInputFocus,
|
||||
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
|
||||
SuggestAlternatives.OtherSuggestions,
|
||||
SuggestContext.Visible.toNegated(),
|
||||
|
|
|
@ -511,6 +511,8 @@ export class SuggestModel implements IDisposable {
|
|||
if (!suggestOptions.showFolders) { result.add(CompletionItemKind.Folder); }
|
||||
if (!suggestOptions.showTypeParameters) { result.add(CompletionItemKind.TypeParameter); }
|
||||
if (!suggestOptions.showSnippets) { result.add(CompletionItemKind.Snippet); }
|
||||
if (!suggestOptions.showUsers) { result.add(CompletionItemKind.User); }
|
||||
if (!suggestOptions.showIssues) { result.add(CompletionItemKind.Issue); }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ import { ITextModel } from 'vs/editor/common/model';
|
|||
import { DocumentRangeSemanticTokensProviderRegistry, DocumentRangeSemanticTokensProvider, SemanticTokens } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { toMultilineTokens2, SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from 'vs/editor/common/services/modelServiceImpl';
|
||||
|
||||
class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution {
|
||||
|
||||
|
@ -28,7 +31,9 @@ class ViewportSemanticTokensContribution extends Disposable implements IEditorCo
|
|||
|
||||
constructor(
|
||||
editor: ICodeEditor,
|
||||
@IModelService private readonly _modelService: IModelService
|
||||
@IModelService private readonly _modelService: IModelService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
this._editor = editor;
|
||||
|
@ -49,6 +54,16 @@ class ViewportSemanticTokensContribution extends Disposable implements IEditorCo
|
|||
this._cancelAll();
|
||||
this._tokenizeViewport.schedule();
|
||||
}));
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(SEMANTIC_HIGHLIGHTING_SETTING_ID)) {
|
||||
this._cancelAll();
|
||||
this._tokenizeViewport.schedule();
|
||||
}
|
||||
}));
|
||||
this._register(this._themeService.onDidColorThemeChange(() => {
|
||||
this._cancelAll();
|
||||
this._tokenizeViewport.schedule();
|
||||
}));
|
||||
}
|
||||
|
||||
private static _getSemanticColoringProvider(model: ITextModel): DocumentRangeSemanticTokensProvider | null {
|
||||
|
@ -80,12 +95,15 @@ class ViewportSemanticTokensContribution extends Disposable implements IEditorCo
|
|||
if (model.hasSemanticTokens()) {
|
||||
return;
|
||||
}
|
||||
if (!isSemanticColoringEnabled(model, this._themeService, this._configurationService)) {
|
||||
return;
|
||||
}
|
||||
const provider = ViewportSemanticTokensContribution._getSemanticColoringProvider(model);
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
const styling = this._modelService.getSemanticTokensProviderStyling(provider);
|
||||
const visibleRanges = this._editor.getVisibleRanges();
|
||||
const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow();
|
||||
|
||||
this._outstandingRequests = this._outstandingRequests.concat(visibleRanges.map(range => this._requestRange(model, range, provider, styling)));
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
|||
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
|
||||
import { StandaloneQuickInputServiceImpl } from 'vs/editor/standalone/browser/quickInput/standaloneQuickInputServiceImpl';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
|
||||
export interface IEditorOverrideServices {
|
||||
[index: string]: any;
|
||||
|
@ -166,6 +167,8 @@ export module StaticServices {
|
|||
|
||||
export const storageService = define(IStorageService, () => new InMemoryStorageService());
|
||||
|
||||
export const storageSyncService = define(IStorageKeysSyncRegistryService, () => new StorageKeysSyncRegistryService());
|
||||
|
||||
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o)));
|
||||
}
|
||||
|
||||
|
|
|
@ -323,4 +323,68 @@ suite('TokensStore', () => {
|
|||
assert.equal(lineTokens.getCount(), 3);
|
||||
});
|
||||
|
||||
test('issue #94133: Semantic colors stick around when using (only) range provider', () => {
|
||||
const store = new TokensStore2();
|
||||
|
||||
// setPartial: [1,1 -> 1,20] [(1,9-11)]
|
||||
store.setPartial(new Range(1, 1, 1, 20), [
|
||||
new MultilineTokens2(1, new SparseEncodedTokens(new Uint32Array([
|
||||
0, 9, 11, 1,
|
||||
])))
|
||||
]);
|
||||
|
||||
// setPartial: [1,1 -> 1,20], []
|
||||
store.setPartial(new Range(1, 1, 1, 20), []);
|
||||
|
||||
const lineTokens = store.addSemanticTokens(1, new LineTokens(new Uint32Array([12, 1]), `enum Enum1 {`));
|
||||
assert.equal(lineTokens.getCount(), 1);
|
||||
});
|
||||
|
||||
test('bug', () => {
|
||||
function createTokens(str: string): MultilineTokens2 {
|
||||
str = str.replace(/^\[\(/, '');
|
||||
str = str.replace(/\)\]$/, '');
|
||||
const strTokens = str.split('),(');
|
||||
let result: number[] = [];
|
||||
let firstLineNumber = 0;
|
||||
for (const strToken of strTokens) {
|
||||
const pieces = strToken.split(',');
|
||||
const chars = pieces[1].split('-');
|
||||
const lineNumber = parseInt(pieces[0], 10);
|
||||
const startChar = parseInt(chars[0], 10);
|
||||
const endChar = parseInt(chars[1], 10);
|
||||
if (firstLineNumber === 0) {
|
||||
// this is the first line
|
||||
firstLineNumber = lineNumber;
|
||||
}
|
||||
result.push(lineNumber - firstLineNumber, startChar, endChar, (lineNumber + startChar) % 13);
|
||||
}
|
||||
return new MultilineTokens2(firstLineNumber, new SparseEncodedTokens(new Uint32Array(result)));
|
||||
}
|
||||
|
||||
const store = new TokensStore2();
|
||||
// setPartial [36446,1 -> 36475,115] [(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35),(36470,38-46),(36473,25-35),(36473,36-51),(36474,28-33),(36474,36-49),(36474,50-58),(36475,35-53),(36475,54-62)]
|
||||
store.setPartial(
|
||||
new Range(36446, 1, 36475, 115),
|
||||
[createTokens('[(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35),(36470,38-46),(36473,25-35),(36473,36-51),(36474,28-33),(36474,36-49),(36474,50-58),(36475,35-53),(36475,54-62)]')]
|
||||
);
|
||||
// setPartial [36436,1 -> 36464,142] [(36437,33-37),(36437,38-42),(36437,47-57),(36437,58-67),(36438,35-53),(36438,54-62),(36440,24-29),(36440,33-46),(36440,47-53),(36442,25-35),(36442,36-50),(36443,30-39),(36443,42-46),(36443,47-53),(36443,54-58),(36443,63-73),(36443,74-84),(36443,87-91),(36443,92-98),(36443,101-105),(36443,106-112),(36443,113-119),(36444,28-37),(36444,38-42),(36444,47-57),(36444,58-75),(36444,80-95),(36444,96-105),(36445,35-53),(36445,54-62),(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62)]
|
||||
store.setPartial(
|
||||
new Range(36436, 1, 36464, 142),
|
||||
[createTokens('[(36437,33-37),(36437,38-42),(36437,47-57),(36437,58-67),(36438,35-53),(36438,54-62),(36440,24-29),(36440,33-46),(36440,47-53),(36442,25-35),(36442,36-50),(36443,30-39),(36443,42-46),(36443,47-53),(36443,54-58),(36443,63-73),(36443,74-84),(36443,87-91),(36443,92-98),(36443,101-105),(36443,106-112),(36443,113-119),(36444,28-37),(36444,38-42),(36444,47-57),(36444,58-75),(36444,80-95),(36444,96-105),(36445,35-53),(36445,54-62),(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62)]')]
|
||||
);
|
||||
// setPartial [36457,1 -> 36485,140] [(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35),(36470,38-46),(36473,25-35),(36473,36-51),(36474,28-33),(36474,36-49),(36474,50-58),(36475,35-53),(36475,54-62),(36477,28-32),(36477,33-37),(36477,42-52),(36477,53-69),(36478,32-36),(36478,37-41),(36478,46-56),(36478,57-74),(36479,32-36),(36479,37-41),(36479,46-56),(36479,57-76),(36480,32-36),(36480,37-41),(36480,46-56),(36480,57-68),(36481,32-36),(36481,37-41),(36481,46-56),(36481,57-68),(36482,39-57),(36482,58-66),(36484,34-38),(36484,39-45),(36484,46-50),(36484,55-65),(36484,66-82),(36484,86-97),(36484,98-102),(36484,103-109),(36484,111-124),(36484,125-133),(36485,39-57),(36485,58-66)]
|
||||
store.setPartial(
|
||||
new Range(36457, 1, 36485, 140),
|
||||
[createTokens('[(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35),(36470,38-46),(36473,25-35),(36473,36-51),(36474,28-33),(36474,36-49),(36474,50-58),(36475,35-53),(36475,54-62),(36477,28-32),(36477,33-37),(36477,42-52),(36477,53-69),(36478,32-36),(36478,37-41),(36478,46-56),(36478,57-74),(36479,32-36),(36479,37-41),(36479,46-56),(36479,57-76),(36480,32-36),(36480,37-41),(36480,46-56),(36480,57-68),(36481,32-36),(36481,37-41),(36481,46-56),(36481,57-68),(36482,39-57),(36482,58-66),(36484,34-38),(36484,39-45),(36484,46-50),(36484,55-65),(36484,66-82),(36484,86-97),(36484,98-102),(36484,103-109),(36484,111-124),(36484,125-133),(36485,39-57),(36485,58-66)]')]
|
||||
);
|
||||
// setPartial [36441,1 -> 36469,56] [(36442,25-35),(36442,36-50),(36443,30-39),(36443,42-46),(36443,47-53),(36443,54-58),(36443,63-73),(36443,74-84),(36443,87-91),(36443,92-98),(36443,101-105),(36443,106-112),(36443,113-119),(36444,28-37),(36444,38-42),(36444,47-57),(36444,58-75),(36444,80-95),(36444,96-105),(36445,35-53),(36445,54-62),(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35)]
|
||||
store.setPartial(
|
||||
new Range(36441, 1, 36469, 56),
|
||||
[createTokens('[(36442,25-35),(36442,36-50),(36443,30-39),(36443,42-46),(36443,47-53),(36443,54-58),(36443,63-73),(36443,74-84),(36443,87-91),(36443,92-98),(36443,101-105),(36443,106-112),(36443,113-119),(36444,28-37),(36444,38-42),(36444,47-57),(36444,58-75),(36444,80-95),(36444,96-105),(36445,35-53),(36445,54-62),(36448,24-29),(36448,33-46),(36448,47-54),(36450,25-35),(36450,36-50),(36451,28-33),(36451,36-49),(36451,50-57),(36452,35-53),(36452,54-62),(36454,33-38),(36454,41-54),(36454,55-60),(36455,35-53),(36455,54-62),(36457,33-44),(36457,45-49),(36457,50-56),(36457,62-83),(36457,84-88),(36458,35-53),(36458,54-62),(36460,33-37),(36460,38-42),(36460,47-57),(36460,58-67),(36461,35-53),(36461,54-62),(36463,34-38),(36463,39-45),(36463,46-51),(36463,54-63),(36463,64-71),(36463,76-80),(36463,81-87),(36463,88-92),(36463,97-107),(36463,108-119),(36464,35-53),(36464,54-62),(36466,33-71),(36466,72-76),(36467,35-53),(36467,54-62),(36469,24-29),(36469,33-46),(36469,47-54),(36470,24-35)]')]
|
||||
);
|
||||
|
||||
const lineTokens = store.addSemanticTokens(36451, new LineTokens(new Uint32Array([60, 1]), ` if (flags & ModifierFlags.Ambient) {`));
|
||||
assert.equal(lineTokens.getCount(), 7);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,10 +25,10 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common
|
|||
|
||||
export interface ICommandQuickPick extends IPickerQuickAccessItem {
|
||||
commandId: string;
|
||||
commandAlias: string | undefined;
|
||||
commandAlias?: string;
|
||||
}
|
||||
|
||||
export interface ICommandsQuickAccessOptions extends IPickerQuickAccessProviderOptions {
|
||||
export interface ICommandsQuickAccessOptions extends IPickerQuickAccessProviderOptions<ICommandQuickPick> {
|
||||
showAlias: boolean;
|
||||
}
|
||||
|
||||
|
@ -122,8 +122,8 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
|||
const commandPick = filteredCommandPicks[i];
|
||||
const keybinding = this.keybindingService.lookupKeybinding(commandPick.commandId);
|
||||
const ariaLabel = keybinding ?
|
||||
localize('commandPickAriaLabelWithKeybinding', "{0}, {1}, commands picker", commandPick.label, keybinding.getAriaLabel()) :
|
||||
localize('commandPickAriaLabel', "{0}, commands picker", commandPick.label);
|
||||
localize('commandPickAriaLabelWithKeybinding', "{0}, {1}", commandPick.label, keybinding.getAriaLabel()) :
|
||||
commandPick.label;
|
||||
|
||||
// Separator: recently used
|
||||
if (i === 0 && this.commandsHistory.peek(commandPick.commandId)) {
|
||||
|
|
|
@ -77,7 +77,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
|||
(helpEntry.needsEditor ? editorProviders : globalProviders).push({
|
||||
prefix,
|
||||
label,
|
||||
ariaLabel: localize('entryAriaLabel', "{0}, quick access help picker", label),
|
||||
ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description),
|
||||
description: helpEntry.description
|
||||
});
|
||||
}
|
||||
|
|
|
@ -59,8 +59,17 @@ export interface IPickerQuickAccessItem extends IQuickPickItem {
|
|||
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
|
||||
}
|
||||
|
||||
export interface IPickerQuickAccessProviderOptions {
|
||||
export interface IPickerQuickAccessProviderOptions<T extends IPickerQuickAccessItem> {
|
||||
|
||||
/**
|
||||
* Enables support for opening picks in the background via gesture.
|
||||
*/
|
||||
canAcceptInBackground?: boolean;
|
||||
|
||||
/**
|
||||
* Enables to show a pick entry when no results are returned from a search.
|
||||
*/
|
||||
noResultsPick?: T;
|
||||
}
|
||||
|
||||
export type Pick<T> = T | IQuickPickSeparator;
|
||||
|
@ -85,7 +94,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
|
|||
|
||||
private static FAST_PICKS_RACE_DELAY = 200; // timeout before we accept fast results before slow results are present
|
||||
|
||||
constructor(private prefix: string, protected options?: IPickerQuickAccessProviderOptions) {
|
||||
constructor(private prefix: string, protected options?: IPickerQuickAccessProviderOptions<T>) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -113,9 +122,10 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
|
|||
|
||||
// Collect picks and support both long running and short or combined
|
||||
const picksToken = picksCts.token;
|
||||
const providedPicks = this.getPicks(picker.value.substr(this.prefix.length).trim(), picksDisposables, picksToken);
|
||||
const picksFilter = picker.value.substr(this.prefix.length).trim();
|
||||
const providedPicks = this.getPicks(picksFilter, picksDisposables, picksToken);
|
||||
|
||||
function applyPicks(picks: Picks<T>, skipEmpty?: boolean): boolean {
|
||||
const applyPicks = (picks: Picks<T>, skipEmpty?: boolean): boolean => {
|
||||
let items: ReadonlyArray<Pick<T>>;
|
||||
let activeItem: T | undefined = undefined;
|
||||
|
||||
|
@ -126,8 +136,14 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
|
|||
items = picks;
|
||||
}
|
||||
|
||||
if (items.length === 0 && skipEmpty) {
|
||||
return false;
|
||||
if (items.length === 0) {
|
||||
if (skipEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (picksFilter.length > 0 && this.options?.noResultsPick) {
|
||||
items = [this.options.noResultsPick];
|
||||
}
|
||||
}
|
||||
|
||||
picker.items = items;
|
||||
|
@ -136,7 +152,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// No Picks
|
||||
if (providedPicks === null) {
|
||||
|
|
|
@ -90,9 +90,14 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||
picker.placeholder = descriptor?.placeholder;
|
||||
picker.quickNavigate = options?.quickNavigateConfiguration;
|
||||
picker.hideInput = !!picker.quickNavigate && !visibleQuickAccess; // only hide input if there was no picker opened already
|
||||
picker.itemActivation = options?.itemActivation || (options?.quickNavigateConfiguration ? ItemActivation.SECOND : ItemActivation.FIRST);
|
||||
if (typeof options?.itemActivation === 'number' || options?.quickNavigateConfiguration) {
|
||||
picker.itemActivation = options?.itemActivation ?? ItemActivation.SECOND /* quick nav is always second */;
|
||||
}
|
||||
picker.contextKey = descriptor?.contextKey;
|
||||
picker.filterValue = (value: string) => value.substring(descriptor ? descriptor.prefix.length : 0);
|
||||
if (descriptor?.placeholder) {
|
||||
picker.ariaLabel = descriptor?.placeholder;
|
||||
}
|
||||
|
||||
// Register listeners
|
||||
const cancellationToken = this.registerPickerListeners(picker, provider, descriptor, value, disposables);
|
||||
|
|
|
@ -3164,7 +3164,7 @@ declare module 'vscode' {
|
|||
*/
|
||||
public readonly tokenModifiers: string[];
|
||||
|
||||
constructor(tokenTypes: string[], tokenModifiers: string[]);
|
||||
constructor(tokenTypes: string[], tokenModifiers?: string[]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3184,7 +3184,7 @@ declare module 'vscode' {
|
|||
* @param tokenType The encoded token type.
|
||||
* @param tokenModifiers The encoded token modifiers.
|
||||
*/
|
||||
push(line: number, char: number, length: number, tokenType: number, tokenModifiers: number): void;
|
||||
push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void;
|
||||
|
||||
/**
|
||||
* Add another token. Use only when providing a legend.
|
||||
|
@ -3203,8 +3203,8 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* Represents semantic tokens, either in a range or in an entire document.
|
||||
* See [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens) for an explanation of the format.
|
||||
* See `SemanticTokensBuilder` for a helper to create an instance.
|
||||
* @see [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens) for an explanation of the format.
|
||||
* @see [SemanticTokensBuilder](#SemanticTokensBuilder) for a helper to create an instance.
|
||||
*/
|
||||
export class SemanticTokens {
|
||||
/**
|
||||
|
@ -3215,7 +3215,7 @@ declare module 'vscode' {
|
|||
readonly resultId?: string;
|
||||
/**
|
||||
* The actual tokens data.
|
||||
* See [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens) for an explanation of the format.
|
||||
* @see [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens) for an explanation of the format.
|
||||
*/
|
||||
readonly data: Uint32Array;
|
||||
|
||||
|
@ -3224,7 +3224,7 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* Represents edits to semantic tokens.
|
||||
* See [provideDocumentSemanticTokensEdits](#DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits) for an explanation of the format.
|
||||
* @see [provideDocumentSemanticTokensEdits](#DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits) for an explanation of the format.
|
||||
*/
|
||||
export class SemanticTokensEdits {
|
||||
/**
|
||||
|
@ -3244,7 +3244,7 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* Represents an edit to semantic tokens.
|
||||
* See [provideDocumentSemanticTokensEdits](#DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits) for an explanation of the format.
|
||||
* @see [provideDocumentSemanticTokensEdits](#DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits) for an explanation of the format.
|
||||
*/
|
||||
export class SemanticTokensEdit {
|
||||
/**
|
||||
|
@ -3274,11 +3274,8 @@ declare module 'vscode' {
|
|||
onDidChangeSemanticTokens?: Event<void>;
|
||||
|
||||
/**
|
||||
* A file can contain many tokens, perhaps even hundreds of thousands of tokens. Therefore, to improve
|
||||
* the memory consumption around describing semantic tokens, we have decided to avoid allocating an object
|
||||
* for each token and we represent tokens from a file as an array of integers. Furthermore, the position
|
||||
* of each token is expressed relative to the token before it because most tokens remain stable relative to
|
||||
* each other when edits are made in a file.
|
||||
* Tokens in a file are represented as an array of integers. The position of each token is expressed relative to
|
||||
* the token before it, because most tokens remain stable relative to each other when edits are made in a file.
|
||||
*
|
||||
* ---
|
||||
* In short, each token takes 5 integers to represent, so a specific token `i` in the file consists of the following array indices:
|
||||
|
@ -3331,6 +3328,7 @@ declare module 'vscode' {
|
|||
* [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ]
|
||||
* ```
|
||||
*
|
||||
* @see [SemanticTokensBuilder](#SemanticTokensBuilder) for a helper to encode tokens as integers.
|
||||
* *NOTE*: When doing edits, it is possible that multiple edits occur until VS Code decides to invoke the semantic tokens provider.
|
||||
* *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'.
|
||||
*/
|
||||
|
@ -3338,21 +3336,18 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* Instead of always returning all the tokens in a file, it is possible for a `DocumentSemanticTokensProvider` to implement
|
||||
* this method (`updateSemanticTokens`) and then return incremental updates to the previously provided semantic tokens.
|
||||
* this method (`provideDocumentSemanticTokensEdits`) and then return incremental updates to the previously provided semantic tokens.
|
||||
*
|
||||
* ---
|
||||
* ### How tokens change when the document changes
|
||||
*
|
||||
* Let's look at how tokens might change.
|
||||
* Suppose that `provideDocumentSemanticTokens` has previously returned the following semantic tokens:
|
||||
* ```
|
||||
* // 1st token, 2nd token, 3rd token
|
||||
* [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ]
|
||||
* ```
|
||||
*
|
||||
* Continuing with the above example, suppose a new line was inserted at the top of the file.
|
||||
* That would make all the tokens move down by one line (notice how the line has changed for each one):
|
||||
* ```
|
||||
* { line: 3, startChar: 5, length: 3, tokenType: "property", tokenModifiers: ["private", "static"] },
|
||||
* { line: 3, startChar: 10, length: 4, tokenType: "type", tokenModifiers: [] },
|
||||
* { line: 6, startChar: 2, length: 7, tokenType: "class", tokenModifiers: [] }
|
||||
* ```
|
||||
* The integer encoding of the tokens does not change substantially because of the delta-encoding of positions:
|
||||
* Also suppose that after some edits, the new semantic tokens in a file are:
|
||||
* ```
|
||||
* // 1st token, 2nd token, 3rd token
|
||||
* [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ]
|
||||
|
@ -3365,26 +3360,6 @@ declare module 'vscode' {
|
|||
* edit: { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3
|
||||
* ```
|
||||
*
|
||||
* Furthermore, let's assume that a new token has appeared on line 4:
|
||||
* ```
|
||||
* { line: 3, startChar: 5, length: 3, tokenType: "property", tokenModifiers: ["private", "static"] },
|
||||
* { line: 3, startChar: 10, length: 4, tokenType: "type", tokenModifiers: [] },
|
||||
* { line: 4, startChar: 3, length: 5, tokenType: "property", tokenModifiers: ["static"] },
|
||||
* { line: 6, startChar: 2, length: 7, tokenType: "class", tokenModifiers: [] }
|
||||
* ```
|
||||
* The integer encoding of the tokens is:
|
||||
* ```
|
||||
* // 1st token, 2nd token, 3rd token, 4th token
|
||||
* [ 3,5,3,0,3, 0,5,4,1,0, 1,3,5,0,2, 2,2,7,2,0, ]
|
||||
* ```
|
||||
* Again, it is possible to express these new tokens in terms of an edit applied to the previous tokens:
|
||||
* ```
|
||||
* [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // old tokens
|
||||
* [ 3,5,3,0,3, 0,5,4,1,0, 1,3,5,0,2, 2,2,7,2,0, ] // new tokens
|
||||
*
|
||||
* edit: { start: 10, deleteCount: 1, data: [1,3,5,0,2,2] } // replace integer at offset 10 with [1,3,5,0,2,2]
|
||||
* ```
|
||||
*
|
||||
* *NOTE*: If the provider cannot compute `SemanticTokensEdits`, it can "give up" and return all the tokens in the document again.
|
||||
* *NOTE*: All edits in `SemanticTokensEdits` contain indices in the old integers array, so they all refer to the previous result state.
|
||||
*/
|
||||
|
@ -3397,7 +3372,7 @@ declare module 'vscode' {
|
|||
*/
|
||||
export interface DocumentRangeSemanticTokensProvider {
|
||||
/**
|
||||
* See [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens).
|
||||
* @see [provideDocumentSemanticTokens](#DocumentSemanticTokensProvider.provideDocumentSemanticTokens).
|
||||
*/
|
||||
provideDocumentRangeSemanticTokens(document: TextDocument, range: Range, token: CancellationToken): ProviderResult<SemanticTokens>;
|
||||
}
|
||||
|
@ -5233,6 +5208,9 @@ declare module 'vscode' {
|
|||
* [`Command`](#Command) or identifier of a command to run on click.
|
||||
*
|
||||
* The command must be [known](#commands.getCommands).
|
||||
*
|
||||
* Note that if this is a [`Command`](#Command) object, only the [`command`](#Command.command) and [`arguments`](#Command.arguments)
|
||||
* are used by VS Code.
|
||||
*/
|
||||
command: string | Command | undefined;
|
||||
|
||||
|
@ -6840,9 +6818,6 @@ declare module 'vscode' {
|
|||
* Text based custom editors use a [`TextDocument`](#TextDocument) as their data model. This considerably simplifies
|
||||
* implementing a custom editor as it allows VS Code to handle many common operations such as
|
||||
* undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`.
|
||||
*
|
||||
* You should use text based custom editors when dealing with text based file formats, such as `xml` or `json`.
|
||||
* For binary files or more specialized use cases, see [CustomEditorProvider](#CustomEditorProvider).
|
||||
*/
|
||||
export interface CustomTextEditorProvider {
|
||||
|
||||
|
@ -6865,259 +6840,6 @@ declare module 'vscode' {
|
|||
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the editing capability of a custom editor. This allows the custom editor to hook into standard
|
||||
* editor events such as `undo` or `save`.
|
||||
*
|
||||
* @param EditType Type of edits used for the documents this delegate handles.
|
||||
*/
|
||||
interface CustomEditorEditingDelegate<EditType = unknown> {
|
||||
/**
|
||||
* Save the resource.
|
||||
*
|
||||
* @param document Document to save.
|
||||
* @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
|
||||
*
|
||||
* @return Thenable signaling that the save has completed.
|
||||
*/
|
||||
save(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Save the existing resource at a new path.
|
||||
*
|
||||
* @param document Document to save.
|
||||
* @param targetResource Location to save to.
|
||||
*
|
||||
* @return Thenable signaling that the save has completed.
|
||||
*/
|
||||
saveAs(document: CustomDocument<EditType>, targetResource: Uri): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Event triggered by extensions to signal to VS Code that an edit has occurred.
|
||||
*/
|
||||
readonly onDidEdit: Event<CustomDocumentEditEvent<EditType>>;
|
||||
|
||||
/**
|
||||
* Apply a set of edits.
|
||||
*
|
||||
* Note that is not invoked when `onDidEdit` is called because `onDidEdit` implies also updating the view to reflect the edit.
|
||||
*
|
||||
* @param document Document to apply edits to.
|
||||
* @param edit Array of edits. Sorted from oldest to most recent.
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
applyEdits(document: CustomDocument<EditType>, edits: ReadonlyArray<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Undo a set of edits.
|
||||
*
|
||||
* This is triggered when a user undoes an edit.
|
||||
*
|
||||
* @param document Document to undo edits from.
|
||||
* @param edit Array of edits. Sorted from most recent to oldest.
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
undoEdits(document: CustomDocument<EditType>, edits: ReadonlyArray<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Revert the file to its last saved state.
|
||||
*
|
||||
* @param document Document to revert.
|
||||
* @param edits Added or applied edits.
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
revert(document: CustomDocument<EditType>, edits: CustomDocumentRevert<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Back up the resource in its current state.
|
||||
*
|
||||
* Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
|
||||
* its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
|
||||
* the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource,
|
||||
* your extension should first check to see if any backups exist for the resource. If there is a backup, your
|
||||
* extension should load the file contents from there instead of from the resource in the workspace.
|
||||
*
|
||||
* `backup` is triggered whenever an edit it made. Calls to `backup` are debounced so that if multiple edits are
|
||||
* made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
|
||||
* `auto save` is enabled (since auto save already persists resource ).
|
||||
*
|
||||
* @param document Document to backup.
|
||||
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
|
||||
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
|
||||
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
|
||||
* than cancelling it to ensure that VS Code has some valid backup.
|
||||
*/
|
||||
backup(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event triggered by extensions to signal to VS Code that an edit has occurred on a `CustomDocument`.
|
||||
*
|
||||
* @param EditType Type of edits used for the document.
|
||||
*/
|
||||
interface CustomDocumentEditEvent<EditType = unknown> {
|
||||
/**
|
||||
* Document the edit is for.
|
||||
*/
|
||||
readonly document: CustomDocument<EditType>;
|
||||
|
||||
/**
|
||||
* Object that describes the edit.
|
||||
*
|
||||
* Edit objects are passed back to your extension in `CustomEditorEditingDelegate.undoEdits`,
|
||||
* `CustomEditorEditingDelegate.applyEdits`, and `CustomEditorEditingDelegate.revert`.
|
||||
*/
|
||||
readonly edit: EditType;
|
||||
|
||||
/**
|
||||
* Display name describing the edit.
|
||||
*/
|
||||
readonly label?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delta for edits undone/redone while reverting for a `CustomDocument`.
|
||||
*
|
||||
* @param EditType Type of edits used for the document being reverted.
|
||||
*/
|
||||
interface CustomDocumentRevert<EditType = unknown> {
|
||||
/**
|
||||
* List of edits that were undone to get the document back to its on disk state.
|
||||
*/
|
||||
readonly undoneEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* List of edits that were reapplied to get the document back to its on disk state.
|
||||
*/
|
||||
readonly appliedEdits: ReadonlyArray<EditType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom document used by a [`CustomEditorProvider`](#CustomEditorProvider).
|
||||
*
|
||||
* All custom documents must subclass `CustomDocument`. Custom documents are only used within a given
|
||||
* `CustomEditorProvider`. The lifecycle of a `CustomDocument` is managed by VS Code. When no more references
|
||||
* remain to a `CustomDocument`, it is disposed of.
|
||||
*
|
||||
* @param EditType Type of edits used in this document.
|
||||
*/
|
||||
class CustomDocument<EditType = unknown> {
|
||||
/**
|
||||
* @param uri The associated resource for this document.
|
||||
*/
|
||||
constructor(uri: Uri);
|
||||
|
||||
/**
|
||||
* The associated uri for this document.
|
||||
*/
|
||||
readonly uri: Uri;
|
||||
|
||||
/**
|
||||
* Is this document representing an untitled file which has never been saved yet.
|
||||
*/
|
||||
readonly isUntitled: boolean;
|
||||
|
||||
/**
|
||||
* The version number of this document (it will strictly increase after each
|
||||
* change, including undo/redo).
|
||||
*/
|
||||
readonly version: number;
|
||||
|
||||
/**
|
||||
* `true` if there are unpersisted changes.
|
||||
*/
|
||||
readonly isDirty: boolean;
|
||||
|
||||
/**
|
||||
* List of edits from document open to the document's current state.
|
||||
*
|
||||
* `appliedEdits` returns a copy of the edit stack at the current point in time. Your extension should always
|
||||
* use `CustomDocument.appliedEdits` to check the edit stack instead of holding onto a reference to `appliedEdits`.
|
||||
*/
|
||||
readonly appliedEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* List of edits from document open to the document's last saved point.
|
||||
*
|
||||
* The save point will be behind `appliedEdits` if the user saves and then continues editing,
|
||||
* or in front of the last entry in `appliedEdits` if the user saves and then hits undo.
|
||||
*
|
||||
* `savedEdits` returns a copy of the edit stack at the current point in time. Your extension should always
|
||||
* use `CustomDocument.savedEdits` to check the edit stack instead of holding onto a reference to `savedEdits`.
|
||||
*/
|
||||
readonly savedEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* `true` if the document has been closed. A closed document isn't synchronized anymore
|
||||
* and won't be reused when the same resource is opened again.
|
||||
*/
|
||||
readonly isClosed: boolean;
|
||||
|
||||
/**
|
||||
* Event fired when there are no more references to the `CustomDocument`.
|
||||
*
|
||||
* This happens when all custom editors for the document have been closed. Once a `CustomDocument` is disposed,
|
||||
* it will not be reused when the same resource is opened again.
|
||||
*/
|
||||
readonly onDidDispose: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for custom editors that use a custom document model.
|
||||
*
|
||||
* Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
|
||||
* This gives extensions full control over actions such as edit, save, and backup.
|
||||
*
|
||||
* You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
|
||||
* text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
|
||||
*
|
||||
* @param EditType Type of edits used by the editors of this provider.
|
||||
*/
|
||||
export interface CustomEditorProvider<EditType = unknown> {
|
||||
|
||||
/**
|
||||
* Create a new document for a given resource.
|
||||
*
|
||||
* `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document
|
||||
* is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens.
|
||||
* If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
|
||||
* this point will trigger another call to `openCustomDocument`.
|
||||
*
|
||||
* @param uri Uri of the document to open.
|
||||
* @param token A cancellation token that indicates the result is no longer needed.
|
||||
*
|
||||
* @return The custom document.
|
||||
*/
|
||||
openCustomDocument(uri: Uri, token: CancellationToken): Thenable<CustomDocument<EditType>> | CustomDocument<EditType>;
|
||||
|
||||
/**
|
||||
* Resolve a custom editor for a given resource.
|
||||
*
|
||||
* This is called whenever the user opens a new editor for this `CustomEditorProvider`.
|
||||
*
|
||||
* To resolve a custom editor, the provider must fill in its initial html content and hook up all
|
||||
* the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
|
||||
* for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details.
|
||||
*
|
||||
* @param document Document for the resource being resolved.
|
||||
* @param webviewPanel Webview to resolve.
|
||||
* @param token A cancellation token that indicates the result is no longer needed.
|
||||
*
|
||||
* @return Optional thenable indicating that the custom editor has been resolved.
|
||||
*/
|
||||
resolveCustomEditor(document: CustomDocument<EditType>, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
|
||||
|
||||
/**
|
||||
* Defines the editing capability of the provider.
|
||||
*
|
||||
* When not provided, editors for this provider are considered readonly.
|
||||
*/
|
||||
readonly editingDelegate?: CustomEditorEditingDelegate<EditType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The clipboard provides read and write access to the system's clipboard.
|
||||
*/
|
||||
|
@ -7956,22 +7678,19 @@ declare module 'vscode' {
|
|||
export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable;
|
||||
|
||||
/**
|
||||
* Register a new provider for a custom editor.
|
||||
* Register a provider for custom editors for the `viewType` contributed by the `customEditors` extension point.
|
||||
*
|
||||
* @param viewType Type of the custom editor provider. This should match the `viewType` from the
|
||||
* `package.json` contributions.
|
||||
* When a custom editor is opened, VS Code fires an `onCustomEditor:viewType` activation event. Your extension
|
||||
* must register a [`CustomTextEditorProvider`](#CustomTextEditorProvider) for `viewType` as part of activation.
|
||||
*
|
||||
* @param viewType Unique identifier for the custom editor provider. This should match the `viewType` from the
|
||||
* `customEditors` contribution point.
|
||||
* @param provider Provider that resolves custom editors.
|
||||
* @param options Options for the provider.
|
||||
*
|
||||
* @return Disposable that unregisters the provider.
|
||||
*/
|
||||
export function registerCustomEditorProvider(
|
||||
viewType: string,
|
||||
provider: CustomEditorProvider | CustomTextEditorProvider,
|
||||
options?: {
|
||||
readonly webviewOptions?: WebviewPanelOptions;
|
||||
}
|
||||
): Disposable;
|
||||
export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider, options?: { readonly webviewOptions?: WebviewPanelOptions; }): Disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9861,6 +9580,12 @@ declare module 'vscode' {
|
|||
/**
|
||||
* Register a semantic tokens provider for a document range.
|
||||
*
|
||||
* *Note:* If a document has both a `DocumentSemanticTokensProvider` and a `DocumentRangeSemanticTokensProvider`,
|
||||
* the range provider will be invoked only initially, for the time in which the full document provider takes
|
||||
* to resolve the first request. Once the full document provider resolves the first request, the semantic tokens
|
||||
* provided via the range provider will be discarded and from that point forward, only the document provider
|
||||
* will be used.
|
||||
*
|
||||
* Multiple providers can be registered for a language. In that case providers are sorted
|
||||
* by their [score](#languages.match) and the best-matching provider is used. Failure
|
||||
* of the selected provider will cause a failure of the whole operation.
|
||||
|
|
|
@ -1182,6 +1182,330 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Custom editor https://github.com/microsoft/vscode/issues/77131
|
||||
|
||||
/**
|
||||
* Implements the editing functionality of a custom editor.
|
||||
*
|
||||
* This delegate is how custom editors hook into standard VS Code operations such as save and undo. The delegate
|
||||
* is also how custom editors notify VS Code that an edit has taken place.
|
||||
*
|
||||
* @param EditType Type of edits used for the documents this delegate handles.
|
||||
*/
|
||||
interface CustomEditorEditingDelegate<EditType = unknown> {
|
||||
/**
|
||||
* Save the resource for a custom editor.
|
||||
*
|
||||
* This method is invoked by VS Code when the user saves a custom editor. This can happen when the user
|
||||
* triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled.
|
||||
*
|
||||
* To implement `save`, the delegate must persist the custom editor. This usually means writing the
|
||||
* file data for the custom document to disk. After `save` completes, any associated editor instances will
|
||||
* no longer be marked as dirty.
|
||||
*
|
||||
* @param document Document to save.
|
||||
* @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
|
||||
*
|
||||
* @return Thenable signaling that saving has completed.
|
||||
*/
|
||||
save(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Save the resource for a custom editor to a different location.
|
||||
*
|
||||
* This method is invoked by VS Code when the user triggers `save as` on a custom editor.
|
||||
*
|
||||
* To implement `saveAs`, the delegate must persist the custom editor to `targetResource`. The
|
||||
* existing editor will remain open after `saveAs` completes.
|
||||
*
|
||||
* @param document Document to save.
|
||||
* @param targetResource Location to save to.
|
||||
* @param cancellation Token that signals the save is no longer required.
|
||||
*
|
||||
* @return Thenable signaling that saving has completed.
|
||||
*/
|
||||
saveAs(document: CustomDocument<EditType>, targetResource: Uri, cancellation: CancellationToken): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Signal that an edit has occurred inside a custom editor.
|
||||
*
|
||||
* This event must be fired by your extension whenever an edit happens in a custom editor. An edit can be
|
||||
* anything from changing some text, to cropping an image, to reordering a list. Your extension is free to
|
||||
* define what an edit is and what data is stored on each edit.
|
||||
*
|
||||
* VS Code uses edits to determine if a custom editor is dirty or not. VS Code also passes the edit objects back
|
||||
* to your extension when triggers undo, redo, or revert (using the `undoEdits`, `applyEdits`, and `revert`
|
||||
* methods of `CustomEditorEditingDelegate`)
|
||||
*/
|
||||
readonly onDidEdit: Event<CustomDocumentEditEvent<EditType>>;
|
||||
|
||||
/**
|
||||
* Apply a list of edits to a custom editor.
|
||||
*
|
||||
* This method is invoked by VS Code when the user triggers `redo` in a custom editor.
|
||||
*
|
||||
* To implement `applyEdits`, the delegate must make sure all editor instances (webviews) for `document`
|
||||
* are updated to render the document's new state (that is, every webview must be updated to show the document
|
||||
* after applying `edits` to it).
|
||||
*
|
||||
* Note that `applyEdits` not invoked when `onDidEdit` is fired by your extension because `onDidEdit` implies
|
||||
* that your extension has also updated its editor instances (webviews) to reflect the edit that just occurred.
|
||||
*
|
||||
* @param document Document to apply edits to.
|
||||
* @param redoneEdits Array of edits that were redone. Sorted from oldest to most recent. Use [`document.appliedEdits`](#CustomDocument.appliedEdits)
|
||||
* to get the full set of edits applied to the file (when `applyEdits` is called `appliedEdits` will already include
|
||||
* the newly applied edit at the end).
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
applyEdits(document: CustomDocument<EditType>, redoneEdits: ReadonlyArray<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Undo a list of edits to a custom editor.
|
||||
*
|
||||
* This method is invoked by VS Code when the user triggers `undo` in a custom editor.
|
||||
*
|
||||
* To implement `undoEdits`, the delegate must make sure all editor instances (webviews) for `document`
|
||||
* are updated to render the document's new state (that is, every webview must be updated to show the document
|
||||
* after undoing `edits` from it).
|
||||
*
|
||||
* @param document Document to undo edits from.
|
||||
* @param undoneEdits Array of undone edits. Sorted from most recent to oldest. Use [`document.appliedEdits`](#CustomDocument.appliedEdits)
|
||||
* to get the full set of edits applied to the file (when `undoEdits` is called, `appliedEdits` will already include
|
||||
* have the undone edits removed).
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
undoEdits(document: CustomDocument<EditType>, undoneEdits: ReadonlyArray<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Revert a custom editor to its last saved state.
|
||||
*
|
||||
* This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that
|
||||
* this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file).
|
||||
*
|
||||
* To implement `revert`, the delegate must make sure all editor instances (webviews) for `document`
|
||||
* are displaying the document in the same state is saved in. This usually means reloading the file from the
|
||||
* workspace.
|
||||
*
|
||||
* During `revert`, your extension should also clear any backups for the custom editor. Backups are only needed
|
||||
* when there is a difference between an editor's state in VS Code and its save state on disk.
|
||||
*
|
||||
* @param document Document to revert.
|
||||
* @param revert Object with added or removed edits to get back to the saved state. Use [`document.appliedEdits`](#CustomDocument.appliedEdits)
|
||||
* to get the full set of edits applied to the file (when `revet` is called, `appliedEdits` will already have
|
||||
* removed any edits undone by the revert and added any edits applied by the revert).
|
||||
*
|
||||
* @return Thenable signaling that the change has completed.
|
||||
*/
|
||||
revert(document: CustomDocument<EditType>, revert: CustomDocumentRevert<EditType>): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Back up the resource in its current state.
|
||||
*
|
||||
* Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
|
||||
* its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
|
||||
* the `ExtensionContext.storagePath`. When VS Code reloads and your custom editor is opened for a resource,
|
||||
* your extension should first check to see if any backups exist for the resource. If there is a backup, your
|
||||
* extension should load the file contents from there instead of from the resource in the workspace.
|
||||
*
|
||||
* `backup` is triggered whenever an edit it made. Calls to `backup` are debounced so that if multiple edits are
|
||||
* made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
|
||||
* `auto save` is enabled (since auto save already persists resource ).
|
||||
*
|
||||
* @param document Document to backup.
|
||||
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
|
||||
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
|
||||
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
|
||||
* than cancelling it to ensure that VS Code has some valid backup.
|
||||
*/
|
||||
backup(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event triggered by extensions to signal to VS Code that an edit has occurred on a `CustomDocument`.
|
||||
*
|
||||
* @param EditType Type of edits used for the document.
|
||||
*/
|
||||
interface CustomDocumentEditEvent<EditType = unknown> {
|
||||
/**
|
||||
* Document the edit is for.
|
||||
*/
|
||||
readonly document: CustomDocument<EditType>;
|
||||
|
||||
/**
|
||||
* Object that describes the edit.
|
||||
*
|
||||
* Edit objects are controlled entirely by your extension. Your extension should store whatever information it
|
||||
* needs to on the edit to understand what type of edit was made, how to render that edit, and how to save that
|
||||
* edit to disk.
|
||||
*
|
||||
* Edit objects are passed back to your extension in `CustomEditorEditingDelegate.undoEdits`,
|
||||
* `CustomEditorEditingDelegate.applyEdits`, and `CustomEditorEditingDelegate.revert`. They can also be accessed
|
||||
* using [`CustomDocument.appliedEdits`](#CustomDocument.appliedEdits) and [`CustomDocument.savedEdits`](#CustomDocument.savedEdits).
|
||||
*/
|
||||
readonly edit: EditType;
|
||||
|
||||
/**
|
||||
* Display name describing the edit.
|
||||
*/
|
||||
readonly label?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delta for edits undone/redone while reverting for a `CustomDocument`.
|
||||
*
|
||||
* @param EditType Type of edits used for the document being reverted.
|
||||
*/
|
||||
interface CustomDocumentRevert<EditType = unknown> {
|
||||
/**
|
||||
* List of edits that were undone to get the document back to its on disk state.
|
||||
*/
|
||||
readonly undoneEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* List of edits that were reapplied to get the document back to its on disk state.
|
||||
*/
|
||||
readonly appliedEdits: ReadonlyArray<EditType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom document used by a [`CustomEditorProvider`](#CustomEditorProvider).
|
||||
*
|
||||
* Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is
|
||||
* managed by VS Code. When no more references remain to a `CustomDocument`, it is disposed of.
|
||||
*
|
||||
* @param EditType Type of edits used in this document.
|
||||
*/
|
||||
class CustomDocument<EditType = unknown> {
|
||||
/**
|
||||
* @param uri The associated resource for this document.
|
||||
*/
|
||||
constructor(uri: Uri);
|
||||
|
||||
/**
|
||||
* The associated uri for this document.
|
||||
*/
|
||||
readonly uri: Uri;
|
||||
|
||||
/**
|
||||
* Is this document representing an untitled file which has never been saved yet.
|
||||
*/
|
||||
readonly isUntitled: boolean;
|
||||
|
||||
/**
|
||||
* The version number of this document (it will strictly increase after each
|
||||
* change, including undo/redo).
|
||||
*/
|
||||
readonly version: number;
|
||||
|
||||
/**
|
||||
* `true` if there are unpersisted changes.
|
||||
*/
|
||||
readonly isDirty: boolean;
|
||||
|
||||
/**
|
||||
* List of edits from document open to the document's current state.
|
||||
*
|
||||
* `appliedEdits` returns a copy of the edit stack at the current point in time. Your extension should always
|
||||
* use `CustomDocument.appliedEdits` to check the edit stack instead of holding onto a reference to `appliedEdits`.
|
||||
*/
|
||||
readonly appliedEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* List of edits from document open to the document's last saved point.
|
||||
*
|
||||
* The save point will be behind `appliedEdits` if the user saves and then continues editing,
|
||||
* or in front of the last entry in `appliedEdits` if the user saves and then hits undo.
|
||||
*
|
||||
* `savedEdits` returns a copy of the edit stack at the current point in time. Your extension should always
|
||||
* use `CustomDocument.savedEdits` to check the edit stack instead of holding onto a reference to `savedEdits`.
|
||||
*/
|
||||
readonly savedEdits: ReadonlyArray<EditType>;
|
||||
|
||||
/**
|
||||
* `true` if the document has been closed. A closed document isn't synchronized anymore
|
||||
* and won't be reused when the same resource is opened again.
|
||||
*/
|
||||
readonly isClosed: boolean;
|
||||
|
||||
/**
|
||||
* Event fired when there are no more references to the `CustomDocument`.
|
||||
*
|
||||
* This happens when all custom editors for the document have been closed. Once a `CustomDocument` is disposed,
|
||||
* it will not be reused when the same resource is opened again.
|
||||
*/
|
||||
readonly onDidDispose: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for custom editors that use a custom document model.
|
||||
*
|
||||
* Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
|
||||
* This gives extensions full control over actions such as edit, save, and backup.
|
||||
*
|
||||
* You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
|
||||
* text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
|
||||
*
|
||||
* @param EditType Type of edits used by the editors of this provider.
|
||||
*/
|
||||
export interface CustomEditorProvider<EditType = unknown> {
|
||||
|
||||
/**
|
||||
* Create a new document for a given resource.
|
||||
*
|
||||
* `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document
|
||||
* is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens.
|
||||
* If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
|
||||
* this point will trigger another call to `openCustomDocument`.
|
||||
*
|
||||
* @param uri Uri of the document to open.
|
||||
* @param token A cancellation token that indicates the result is no longer needed.
|
||||
*
|
||||
* @return The custom document.
|
||||
*/
|
||||
openCustomDocument(uri: Uri, token: CancellationToken): Thenable<CustomDocument<EditType>> | CustomDocument<EditType>;
|
||||
|
||||
/**
|
||||
* Resolve a custom editor for a given resource.
|
||||
*
|
||||
* This is called whenever the user opens a new editor for this `CustomEditorProvider`.
|
||||
*
|
||||
* To resolve a custom editor, the provider must fill in its initial html content and hook up all
|
||||
* the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
|
||||
* for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details.
|
||||
*
|
||||
* @param document Document for the resource being resolved.
|
||||
* @param webviewPanel Webview to resolve.
|
||||
* @param token A cancellation token that indicates the result is no longer needed.
|
||||
*
|
||||
* @return Optional thenable indicating that the custom editor has been resolved.
|
||||
*/
|
||||
resolveCustomEditor(document: CustomDocument<EditType>, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
|
||||
|
||||
/**
|
||||
* Defines the editing capability of the provider.
|
||||
*
|
||||
* When not provided, editors for this provider are considered readonly.
|
||||
*/
|
||||
readonly editingDelegate?: CustomEditorEditingDelegate<EditType>;
|
||||
}
|
||||
|
||||
namespace window {
|
||||
/**
|
||||
* Temporary overload for `registerCustomEditorProvider` that takes a `CustomEditorProvider`.
|
||||
*/
|
||||
export function registerCustomEditorProvider2(
|
||||
viewType: string,
|
||||
provider: CustomEditorProvider,
|
||||
options?: {
|
||||
readonly webviewOptions?: WebviewPanelOptions;
|
||||
}
|
||||
): Disposable;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
//#region Custom editor move https://github.com/microsoft/vscode/issues/86146
|
||||
|
||||
// TODO: Also for custom editor
|
||||
|
@ -1352,7 +1676,7 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
export interface NotebookEditorCellEdit {
|
||||
insert(index: number, content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void;
|
||||
insert(index: number, content: string | string[], language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void;
|
||||
delete(index: number): void;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
|
|||
interface AllowedExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
sessionIds?: string[];
|
||||
}
|
||||
|
||||
function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] {
|
||||
|
@ -85,6 +86,16 @@ export class MainThreadAuthenticationProvider extends Disposable {
|
|||
quickPick.onDidAccept(() => {
|
||||
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
|
||||
storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
|
||||
|
||||
// Remove sessions of untrusted extensions
|
||||
const deselectedItems = items.filter(item => !quickPick.selectedItems.includes(item));
|
||||
deselectedItems.forEach(item => {
|
||||
const extensionData = allowedExtensions.find(extension => item.extension.id === extension.id);
|
||||
extensionData?.sessionIds?.forEach(sessionId => {
|
||||
this.logout(sessionId);
|
||||
});
|
||||
});
|
||||
|
||||
quickPick.dispose();
|
||||
});
|
||||
|
||||
|
@ -275,9 +286,19 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
this.authenticationService.sessionsUpdate(id, event);
|
||||
}
|
||||
|
||||
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
let allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (allowList.some(extension => extension.id === extensionId)) {
|
||||
async $getSessionsPrompt(providerId: string, accountName: string, sessionId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
const extensionData = allowList.find(extension => extension.id === extensionId);
|
||||
if (extensionData) {
|
||||
if (!extensionData.sessionIds) {
|
||||
extensionData.sessionIds = [];
|
||||
}
|
||||
|
||||
if (!extensionData.sessionIds.find(id => id === sessionId)) {
|
||||
extensionData.sessionIds.push(sessionId);
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -292,7 +313,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
|
||||
const allow = choice === 1;
|
||||
if (allow) {
|
||||
allowList = allowList.concat({ id: extensionId, name: extensionName });
|
||||
allowList.push({ id: extensionId, name: extensionName, sessionIds: [sessionId] });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
|
@ -313,7 +334,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
|||
}
|
||||
|
||||
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName).concat({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
allowList.push({ id: extensionId, name: extensionName, sessionIds: [] });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
resolveWebview: async (webviewInput): Promise<void> => {
|
||||
const viewType = webviewPanelViewType.toExternal(webviewInput.viewType);
|
||||
if (!viewType) {
|
||||
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(webviewInput.viewType);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -288,7 +288,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -339,7 +339,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
|
||||
let modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, cancellation);
|
||||
let modelRef: IReference<ICustomEditorModel>;
|
||||
try {
|
||||
modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType, cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancellation.isCancellationRequested) {
|
||||
modelRef.dispose();
|
||||
return;
|
||||
|
@ -362,7 +370,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
await this._proxy.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
|
||||
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
modelRef.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -522,14 +531,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
return this._webviewInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
||||
private static getDeserializationFailedContents(viewType: string) {
|
||||
private static getWebviewResolvedFailedContent(viewType: string) {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none';">
|
||||
</head>
|
||||
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", escape(viewType))}</body>
|
||||
<body>${localize('errorMessage', "An error occurred while loading view: {0}", escape(viewType))}</body>
|
||||
</html>`;
|
||||
}
|
||||
}
|
||||
|
@ -795,6 +804,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
return undefined;
|
||||
}
|
||||
// TODO: handle save untitled case
|
||||
// TODO: handle cancellation
|
||||
await createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
|
||||
this.change(() => {
|
||||
this._savePoint = this._currentEditIndex;
|
||||
|
@ -804,7 +814,8 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
|
||||
public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
|
||||
if (this._editable) {
|
||||
await this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource);
|
||||
// TODO: handle cancellation
|
||||
await createCancelablePromise(token => this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource, token));
|
||||
this.change(() => {
|
||||
this._savePoint = this._currentEditIndex;
|
||||
});
|
||||
|
|
|
@ -134,7 +134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors));
|
||||
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
|
||||
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
|
||||
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol, extHostStorage));
|
||||
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
|
||||
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments));
|
||||
|
||||
|
@ -585,7 +585,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
|
||||
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
|
||||
},
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
|
||||
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
|
||||
},
|
||||
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
|
||||
},
|
||||
registerDecorationProvider(provider: vscode.DecorationProvider) {
|
||||
|
|
|
@ -162,7 +162,7 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
|||
$registerAuthenticationProvider(id: string, displayName: string): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, sessionId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;
|
||||
}
|
||||
|
@ -653,7 +653,7 @@ export interface ExtHostWebviewsShape {
|
|||
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void;
|
||||
|
||||
$onSave(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
|
||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void>;
|
||||
|
||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
|
||||
export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
private _proxy: MainThreadAuthenticationShape;
|
||||
|
@ -20,7 +21,8 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
|||
private _onDidChangeSessions = new Emitter<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }>();
|
||||
readonly onDidChangeSessions: Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
constructor(mainContext: IMainContext,
|
||||
@IExtHostStorage private readonly storageService: IExtHostStorage) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
|
||||
}
|
||||
|
||||
|
@ -33,15 +35,34 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
|||
return ids;
|
||||
}
|
||||
|
||||
private async hasNotBeenReadByOtherExtension(providerId: string, session: vscode.AuthenticationSession, extensionId: string): Promise<boolean> {
|
||||
const readerId = await this.storageService.getValue(true, `${providerId}-${session.accountName}-${session.id}`);
|
||||
if (!readerId) {
|
||||
await this.storageService.setValue(true, `${providerId}-${session.accountName}-${session.id}`, extensionId as any);
|
||||
return true;
|
||||
}
|
||||
|
||||
return readerId === extensionId;
|
||||
}
|
||||
|
||||
private async isMatchingSession(session: vscode.AuthenticationSession, scopes: string, providerId: string, extensionId: string): Promise<boolean> {
|
||||
return session.scopes.sort().join(' ') === scopes && (await this.hasNotBeenReadByOtherExtension(providerId, session, extensionId));
|
||||
}
|
||||
|
||||
async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise<readonly vscode.AuthenticationSession[]> {
|
||||
const provider = this._authenticationProviders.get(providerId);
|
||||
if (!provider) {
|
||||
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
|
||||
}
|
||||
|
||||
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
|
||||
const orderedScopes = scopes.sort().join(' ');
|
||||
return (await provider.getSessions())
|
||||
.filter(session => session.scopes.sort().join(' ') === orderedScopes)
|
||||
|
||||
const sessions = await provider.getSessions();
|
||||
const filteredSessions = await Promise.all(sessions.map(session => this.isMatchingSession(session, orderedScopes, providerId, extensionId)));
|
||||
|
||||
return sessions
|
||||
.filter((_, i) => { return filteredSessions[i]; })
|
||||
.map(session => {
|
||||
return {
|
||||
id: session.id,
|
||||
|
@ -51,8 +72,9 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
|||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
session.id,
|
||||
provider.displayName,
|
||||
ExtensionIdentifier.toKey(requestingExtension.identifier),
|
||||
extensionId,
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
|
||||
if (!isAllowed) {
|
||||
|
@ -77,9 +99,28 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
|||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
const newSession = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(provider.id, newSession.accountName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
|
||||
return newSession;
|
||||
const session = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(provider.id, session.accountName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
|
||||
return {
|
||||
id: session.id,
|
||||
accountName: session.accountName,
|
||||
scopes: session.scopes,
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
session.id,
|
||||
provider.displayName,
|
||||
ExtensionIdentifier.toKey(requestingExtension.identifier),
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to token access.');
|
||||
}
|
||||
|
||||
return session.getAccessToken();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
|
|
|
@ -402,11 +402,12 @@ export class NotebookEditorCellEdit {
|
|||
}
|
||||
}
|
||||
|
||||
insert(index: number, content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void {
|
||||
insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void {
|
||||
this._throwIfFinalized();
|
||||
|
||||
const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
|
||||
let cell = {
|
||||
source: [content],
|
||||
source: sourceArr,
|
||||
language,
|
||||
cellKind: type,
|
||||
outputs: (outputs as any[]), // TODO@rebornix
|
||||
|
|
|
@ -16,7 +16,7 @@ import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/p
|
|||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { assertIsDefined, isStringArray } from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
|
@ -2420,25 +2420,14 @@ export class SemanticTokensLegend {
|
|||
public readonly tokenTypes: string[];
|
||||
public readonly tokenModifiers: string[];
|
||||
|
||||
constructor(tokenTypes: string[], tokenModifiers: string[]) {
|
||||
constructor(tokenTypes: string[], tokenModifiers: string[] = []) {
|
||||
this.tokenTypes = tokenTypes;
|
||||
this.tokenModifiers = tokenModifiers;
|
||||
}
|
||||
}
|
||||
|
||||
function isStrArrayOrUndefined(arg: any): arg is string[] | undefined {
|
||||
if (typeof arg === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(arg)) {
|
||||
for (const element of arg) {
|
||||
if (typeof element !== 'string') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return ((typeof arg === 'undefined') || isStringArray(arg));
|
||||
}
|
||||
|
||||
export class SemanticTokensBuilder {
|
||||
|
@ -2472,10 +2461,13 @@ export class SemanticTokensBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public push(line: number, char: number, length: number, tokenType: number, tokenModifiers: number): void;
|
||||
public push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void;
|
||||
public push(range: Range, tokenType: string, tokenModifiers?: string[]): void;
|
||||
public push(arg0: any, arg1: any, arg2: any, arg3?: any, arg4?: any): void {
|
||||
if (typeof arg0 === 'number' && typeof arg1 === 'number' && typeof arg2 === 'number' && typeof arg3 === 'number' && typeof arg4 === 'number') {
|
||||
if (typeof arg0 === 'number' && typeof arg1 === 'number' && typeof arg2 === 'number' && typeof arg3 === 'number' && (typeof arg4 === 'number' || typeof arg4 === 'undefined')) {
|
||||
if (typeof arg4 === 'undefined') {
|
||||
arg4 = 0;
|
||||
}
|
||||
// 1st overload
|
||||
return this._pushEncoded(arg0, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
|
|
@ -630,10 +630,10 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
return delegate.save(document, cancellation);
|
||||
}
|
||||
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
|
||||
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise<void> {
|
||||
const delegate = this.getEditingDelegate(viewType);
|
||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||
return delegate.saveAs(document, URI.revive(targetResource));
|
||||
return delegate.saveAs(document, URI.revive(targetResource), cancellation);
|
||||
}
|
||||
|
||||
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
|
||||
|
|
|
@ -41,10 +41,10 @@ namespace schema {
|
|||
case 'menuBar/webNavigation': return MenuId.MenubarWebNavigationMenu;
|
||||
case 'scm/title': return MenuId.SCMTitle;
|
||||
case 'scm/sourceControl': return MenuId.SCMSourceControl;
|
||||
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
|
||||
case 'scm/resourceState/context': return MenuId.SCMResourceContext;//
|
||||
case 'scm/resourceFolder/context': return MenuId.SCMResourceFolderContext;
|
||||
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
|
||||
case 'scm/change/title': return MenuId.SCMChangeContext;
|
||||
case 'scm/change/title': return MenuId.SCMChangeContext;//
|
||||
case 'statusBar/windowIndicator': return MenuId.StatusBarWindowIndicatorMenu;
|
||||
case 'view/title': return MenuId.ViewTitle;
|
||||
case 'view/item/context': return MenuId.ViewItemContext;
|
||||
|
@ -171,6 +171,11 @@ namespace schema {
|
|||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'menuBar/webNavigation': {
|
||||
description: localize('menus.webNavigation', "The top level navigational menu (web only)"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/title': {
|
||||
description: localize('menus.scmTitle', "The Source Control title menu"),
|
||||
type: 'array',
|
||||
|
@ -191,6 +196,16 @@ namespace schema {
|
|||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/resourceFolder/context': {
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/change/title': {
|
||||
description: localize('menus.changeTitle', "The Source Control inline change menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'view/title': {
|
||||
description: localize('view.viewTitle', "The contributed view title menu"),
|
||||
type: 'array',
|
||||
|
|
|
@ -504,6 +504,37 @@ export class ResetViewLocationsAction extends Action {
|
|||
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(ResetViewLocationsAction, ResetViewLocationsAction.ID, ResetViewLocationsAction.LABEL), 'View: Reset View Locations', viewCategory);
|
||||
|
||||
// --- Toggle View with Command
|
||||
export abstract class ToggleViewAction extends Action {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private readonly viewId: string,
|
||||
protected viewsService: IViewsService,
|
||||
protected viewDescriptorService: IViewDescriptorService,
|
||||
protected contextKeyService: IContextKeyService,
|
||||
private layoutService: IWorkbenchLayoutService,
|
||||
cssClass?: string
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const focusedViewId = FocusedViewContext.getValue(this.contextKeyService);
|
||||
|
||||
if (focusedViewId === this.viewId) {
|
||||
if (this.viewDescriptorService.getViewLocation(this.viewId) === ViewContainerLocation.Sidebar) {
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
} else {
|
||||
this.layoutService.setPanelHidden(true);
|
||||
}
|
||||
} else {
|
||||
this.viewsService.openView(this.viewId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Move View with Command
|
||||
export class MoveFocusedViewAction extends Action {
|
||||
static readonly ID = 'workbench.action.moveFocusedView';
|
||||
|
@ -541,6 +572,7 @@ export class MoveFocusedViewAction extends Action {
|
|||
|
||||
const quickPick = this.quickInputService.createQuickPick();
|
||||
quickPick.placeholder = nls.localize('moveFocusedView.selectDestination', "Select a Destination for the View");
|
||||
quickPick.title = nls.localize('moveFocusedView.title', "View: Move {0}", viewDescriptor.name);
|
||||
|
||||
const items: Array<IQuickPickItem | IQuickPickSeparator> = [];
|
||||
|
||||
|
@ -549,10 +581,16 @@ export class MoveFocusedViewAction extends Action {
|
|||
label: nls.localize('sidebar', "Side Bar")
|
||||
});
|
||||
|
||||
items.push({
|
||||
id: '_.sidebar.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInSidebar', "New Container in Side Bar")
|
||||
});
|
||||
const currentContainer = this.viewDescriptorService.getViewContainer(focusedViewId)!;
|
||||
const currentLocation = this.viewDescriptorService.getViewLocation(focusedViewId)!;
|
||||
const isViewSolo = this.viewDescriptorService.getViewDescriptors(currentContainer).allViewDescriptors.length === 1;
|
||||
|
||||
if (!(isViewSolo && currentLocation === ViewContainerLocation.Sidebar)) {
|
||||
items.push({
|
||||
id: '_.sidebar.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInSidebar', "New Container in Side Bar")
|
||||
});
|
||||
}
|
||||
|
||||
const pinnedViewlets = this.activityBarService.getPinnedViewletIds();
|
||||
items.push(...pinnedViewlets
|
||||
|
@ -574,10 +612,13 @@ export class MoveFocusedViewAction extends Action {
|
|||
type: 'separator',
|
||||
label: nls.localize('panel', "Panel")
|
||||
});
|
||||
items.push({
|
||||
id: '_.panel.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"),
|
||||
});
|
||||
|
||||
if (!(isViewSolo && currentLocation === ViewContainerLocation.Panel)) {
|
||||
items.push({
|
||||
id: '_.panel.newcontainer',
|
||||
label: nls.localize('moveFocusedView.newContainerInPanel', "New Container in Panel"),
|
||||
});
|
||||
}
|
||||
|
||||
const pinnedPanels = this.panelService.getPinnedPanels();
|
||||
items.push(...pinnedPanels
|
||||
|
|
|
@ -127,7 +127,7 @@ abstract class BaseOpenRecentAction extends Action {
|
|||
const pick = await this.quickInputService.pick(picks, {
|
||||
contextKey: inRecentFilesPickerContextKey,
|
||||
activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0],
|
||||
placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"),
|
||||
placeHolder: isMacintosh ? nls.localize('openRecentPlaceholderMac', "Select to open (hold Cmd-key to force new window or Alt-key for same window)") : nls.localize('openRecentPlaceholder', "Select to open (hold Ctrl-key to force new window or Alt-key for same window)"),
|
||||
matchOnDescription: true,
|
||||
onKeyMods: mods => keyMods = mods,
|
||||
quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined,
|
||||
|
@ -196,6 +196,7 @@ abstract class BaseOpenRecentAction extends Action {
|
|||
return {
|
||||
iconClasses,
|
||||
label: name,
|
||||
ariaLabel: isDirty ? nls.localize('recentDirtyAriaLabel', "{0}, dirty workspace", name) : name,
|
||||
description: parentPath,
|
||||
buttons: isDirty ? [this.dirtyRecentlyOpened] : [this.removeFromRecentlyOpened],
|
||||
openable,
|
||||
|
|
|
@ -509,8 +509,10 @@ export function containsDragType(event: DragEvent, ...dragTypesToFind: string[])
|
|||
return false;
|
||||
}
|
||||
|
||||
export type Before2D = { verticallyBefore: boolean; horizontallyBefore: boolean; };
|
||||
|
||||
export interface ICompositeDragAndDrop {
|
||||
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent, before?: boolean): void;
|
||||
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent, before?: Before2D): void;
|
||||
onDragOver(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
|
||||
onDragEnter(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): boolean;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,7 @@
|
|||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IPanel } from 'vs/workbench/common/panel';
|
||||
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isAncestor } from 'vs/base/browser/dom';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
|
||||
|
||||
|
@ -85,44 +81,6 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reusable action to toggle a panel with a specific id depending on focus.
|
||||
*/
|
||||
export abstract class TogglePanelAction extends Action {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private readonly panelId: string,
|
||||
protected panelService: IPanelService,
|
||||
private layoutService: IWorkbenchLayoutService,
|
||||
cssClass?: string
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (this.isPanelFocused()) {
|
||||
this.layoutService.setPanelHidden(true);
|
||||
} else {
|
||||
await this.panelService.openPanel(this.panelId, true);
|
||||
}
|
||||
}
|
||||
|
||||
private isPanelActive(): boolean {
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
|
||||
return activePanel?.getId() === this.panelId;
|
||||
}
|
||||
|
||||
private isPanelFocused(): boolean {
|
||||
const activeElement = document.activeElement;
|
||||
const panelPart = this.layoutService.getContainer(Parts.PANEL_PART);
|
||||
|
||||
return !!(this.isPanelActive() && activeElement && panelPart && isAncestor(activeElement, panelPart));
|
||||
}
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
Panels: 'workbench.contributions.panels'
|
||||
};
|
||||
|
|
|
@ -42,6 +42,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
|||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Before2D } from 'vs/workbench/browser/dnd';
|
||||
|
||||
interface IPlaceholderViewlet {
|
||||
id: string;
|
||||
|
@ -152,7 +153,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
|||
hidePart: () => this.layoutService.setSideBarHidden(true),
|
||||
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Sidebar,
|
||||
(id: string, focus?: boolean) => this.viewletService.openViewlet(id, focus),
|
||||
(from: string, to: string, before?: boolean) => this.compositeBar.move(from, to, before)
|
||||
(from: string, to: string, before?: Before2D) => this.compositeBar.move(from, to, before?.verticallyBefore)
|
||||
),
|
||||
compositeSize: 50,
|
||||
colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme),
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { IComposite } from 'vs/workbench/common/composite';
|
||||
import { CompositeDragAndDropData, CompositeDragAndDropObserver, IDraggedCompositeData, ICompositeDragAndDrop } from 'vs/workbench/browser/dnd';
|
||||
import { CompositeDragAndDropData, CompositeDragAndDropObserver, IDraggedCompositeData, ICompositeDragAndDrop, Before2D } from 'vs/workbench/browser/dnd';
|
||||
|
||||
export interface ICompositeBarItem {
|
||||
id: string;
|
||||
|
@ -39,9 +39,9 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop {
|
|||
private viewDescriptorService: IViewDescriptorService,
|
||||
private targetContainerLocation: ViewContainerLocation,
|
||||
private openComposite: (id: string, focus?: boolean) => Promise<IPaneComposite | undefined>,
|
||||
private moveComposite: (from: string, to: string, before?: boolean) => void,
|
||||
private moveComposite: (from: string, to: string, before?: Before2D) => void,
|
||||
) { }
|
||||
drop(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent, before?: boolean): void {
|
||||
drop(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent, before?: Before2D): void {
|
||||
const dragData = data.getData();
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
|
||||
|
@ -255,7 +255,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
|||
},
|
||||
onDrop: (e: IDraggedCompositeData) => {
|
||||
const pinnedItems = this.getPinnedComposites();
|
||||
this.options.dndHandler.drop(e.dragAndDropData, pinnedItems[pinnedItems.length - 1].id, e.eventData, false);
|
||||
this.options.dndHandler.drop(e.dragAndDropData, pinnedItems[pinnedItems.length - 1].id, e.eventData, { horizontallyBefore: false, verticallyBefore: false });
|
||||
toggleClass(parent, 'dragged-over', false);
|
||||
}
|
||||
}));
|
||||
|
|