Archived
Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc (#9646)
* Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc * fix strict
This commit is contained in:
@@ -1,3 +1,16 @@
|
|||||||
|
# {
|
||||||
|
# perform: true,
|
||||||
|
# commands: [
|
||||||
|
# {
|
||||||
|
# type: 'comment',
|
||||||
|
# name: 'findDuplicates',
|
||||||
|
# allowUsers: ['cleidigh', 'usernamehw', 'gjsjohnmurray', 'IllusionMH'],
|
||||||
|
# action: 'comment',
|
||||||
|
# comment: "Potential duplicates:\n${potentialDuplicates}"
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
# }
|
||||||
|
|
||||||
{
|
{
|
||||||
perform: true,
|
perform: true,
|
||||||
commands: [
|
commands: [
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ function markNodes(languageService, options) {
|
|||||||
}
|
}
|
||||||
setColor(node, 2 /* Black */);
|
setColor(node, 2 /* Black */);
|
||||||
black_queue.push(node);
|
black_queue.push(node);
|
||||||
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
|
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
|
||||||
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
|
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
|
||||||
if (references) {
|
if (references) {
|
||||||
for (let i = 0, len = references.length; i < len; i++) {
|
for (let i = 0, len = references.length; i < len; i++) {
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
|
|||||||
setColor(node, NodeColor.Black);
|
setColor(node, NodeColor.Black);
|
||||||
black_queue.push(node);
|
black_queue.push(node);
|
||||||
|
|
||||||
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
|
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) {
|
||||||
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
|
const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth());
|
||||||
if (references) {
|
if (references) {
|
||||||
for (let i = 0, len = references.length; i < len; i++) {
|
for (let i = 0, len = references.length; i < len; i++) {
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@
|
|||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"service-downloader": "0.2.1",
|
"service-downloader": "0.2.1",
|
||||||
"terser": "4.3.8",
|
"terser": "4.3.8",
|
||||||
"typescript": "3.9.0-dev.20200304",
|
"typescript": "^3.9.0-dev.20200313",
|
||||||
"vsce": "1.48.0",
|
"vsce": "1.48.0",
|
||||||
"vscode-telemetry-extractor": "^1.5.4",
|
"vscode-telemetry-extractor": "^1.5.4",
|
||||||
"xml2js": "^0.4.17"
|
"xml2js": "^0.4.17"
|
||||||
|
|||||||
+5
-5
@@ -3676,16 +3676,16 @@ typed-rest-client@^0.9.0:
|
|||||||
tunnel "0.0.4"
|
tunnel "0.0.4"
|
||||||
underscore "1.8.3"
|
underscore "1.8.3"
|
||||||
|
|
||||||
typescript@3.9.0-dev.20200304:
|
|
||||||
version "3.9.0-dev.20200304"
|
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200304.tgz#3cc35357eff29dc5604b4fa56d6597e13daf86ed"
|
|
||||||
integrity sha512-eUip/GgJmjp4qtHiJDxVhE5SDDiPzBUg7KBAFUgb7HgL/tv10JAHej7fnS1i+7xrq1eDtbkJyPaYOVnhL9db7Q==
|
|
||||||
|
|
||||||
typescript@^3.0.1:
|
typescript@^3.0.1:
|
||||||
version "3.5.3"
|
version "3.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
||||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
||||||
|
|
||||||
|
typescript@^3.9.0-dev.20200313:
|
||||||
|
version "3.9.0-dev.20200313"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200313.tgz#f66aeb2c08268f2b1fc6d1d96e15554c6e7ed29b"
|
||||||
|
integrity sha512-85/IJPm1nEUbQDxK3aN+svIy4X3kPcAipihB3704NY1HXncJ1daNLJW1OktOacb8tD/URpIGs9nMgbUrKvglGg==
|
||||||
|
|
||||||
typical@^4.0.0:
|
typical@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
|
resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4"
|
||||||
|
|||||||
@@ -191,9 +191,9 @@ abbrev@1:
|
|||||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
||||||
|
|
||||||
acorn@^6.2.1:
|
acorn@^6.2.1:
|
||||||
version "6.3.0"
|
version "6.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
|
||||||
integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
|
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
|
||||||
|
|
||||||
ajv-errors@^1.0.0:
|
ajv-errors@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
|||||||
@@ -434,5 +434,6 @@
|
|||||||
"terminal.ansiBrightMagenta": "#d778ff",
|
"terminal.ansiBrightMagenta": "#d778ff",
|
||||||
"terminal.ansiBrightCyan": "#78ffff",
|
"terminal.ansiBrightCyan": "#78ffff",
|
||||||
"terminal.ansiBrightWhite": "#ffffff"
|
"terminal.ansiBrightWhite": "#ffffff"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,6 @@
|
|||||||
"menu.foreground": "#CCCCCC",
|
"menu.foreground": "#CCCCCC",
|
||||||
"statusBarItem.remoteForeground": "#FFF",
|
"statusBarItem.remoteForeground": "#FFF",
|
||||||
"statusBarItem.remoteBackground": "#16825D"
|
"statusBarItem.remoteBackground": "#16825D"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
@@ -337,5 +337,6 @@
|
|||||||
"foreground": "#569cd6"
|
"foreground": "#569cd6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
@@ -18,5 +18,6 @@
|
|||||||
"settings.numberInputBorder": "#CECECE",
|
"settings.numberInputBorder": "#CECECE",
|
||||||
"statusBarItem.remoteForeground": "#FFF",
|
"statusBarItem.remoteForeground": "#FFF",
|
||||||
"statusBarItem.remoteBackground": "#16825D"
|
"statusBarItem.remoteBackground": "#16825D"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,5 +394,6 @@
|
|||||||
"foreground": "#dc3958"
|
"foreground": "#dc3958"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -572,5 +572,6 @@
|
|||||||
"foreground": "#c7444a"
|
"foreground": "#c7444a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -476,5 +476,6 @@
|
|||||||
"foreground": "#FD971F"
|
"foreground": "#FD971F"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -494,5 +494,6 @@
|
|||||||
"walkThrough.embeddedEditorBackground": "#00000014",
|
"walkThrough.embeddedEditorBackground": "#00000014",
|
||||||
"editorIndentGuide.background": "#aaaaaa60",
|
"editorIndentGuide.background": "#aaaaaa60",
|
||||||
"editorIndentGuide.activeBackground": "#777777b0"
|
"editorIndentGuide.activeBackground": "#777777b0"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,5 +385,6 @@
|
|||||||
"foreground": "#ec0d1e"
|
"foreground": "#ec0d1e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -477,5 +477,6 @@
|
|||||||
"terminal.ansiBrightMagenta": "#6c71c4",
|
"terminal.ansiBrightMagenta": "#6c71c4",
|
||||||
"terminal.ansiBrightCyan": "#93a1a1",
|
"terminal.ansiBrightCyan": "#93a1a1",
|
||||||
"terminal.ansiBrightWhite": "#fdf6e3"
|
"terminal.ansiBrightWhite": "#fdf6e3"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -484,5 +484,6 @@
|
|||||||
|
|
||||||
// Interactive Playground
|
// Interactive Playground
|
||||||
"walkThrough.embeddedEditorBackground": "#00000014"
|
"walkThrough.embeddedEditorBackground": "#00000014"
|
||||||
}
|
},
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,5 +255,6 @@
|
|||||||
"foreground": "#b267e6"
|
"foreground": "#b267e6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"semanticHighlighting": true
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -178,7 +178,7 @@
|
|||||||
"style-loader": "^1.0.0",
|
"style-loader": "^1.0.0",
|
||||||
"ts-loader": "^4.4.2",
|
"ts-loader": "^4.4.2",
|
||||||
"typemoq": "^0.3.2",
|
"typemoq": "^0.3.2",
|
||||||
"typescript": "3.9.0-dev.20200304",
|
"typescript": "^3.9.0-dev.20200313",
|
||||||
"typescript-formatter": "7.1.0",
|
"typescript-formatter": "7.1.0",
|
||||||
"underscore": "^1.8.2",
|
"underscore": "^1.8.2",
|
||||||
"vinyl": "^2.0.0",
|
"vinyl": "^2.0.0",
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class HeightMap {
|
|||||||
totalSize = viewItem.top + viewItem.height;
|
totalSize = viewItem.top + viewItem.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0);
|
const startingIndex = i;
|
||||||
|
|
||||||
let itemsToInsert: IViewItem[] = [];
|
let itemsToInsert: IViewItem[] = [];
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ export class HeightMap {
|
|||||||
sizeDiff += viewItem.height;
|
sizeDiff += viewItem.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
boundSplice.apply(this.heightMap, itemsToInsert);
|
this.heightMap.splice(startingIndex, 0, ...itemsToInsert);
|
||||||
|
|
||||||
for (j = i; j < this.heightMap.length; j++) {
|
for (j = i; j < this.heightMap.length; j++) {
|
||||||
viewItem = this.heightMap[j];
|
viewItem = this.heightMap[j];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface IIconLabelValueOptions {
|
|||||||
hideIcon?: boolean;
|
hideIcon?: boolean;
|
||||||
extraClasses?: string[];
|
extraClasses?: string[];
|
||||||
italic?: boolean;
|
italic?: boolean;
|
||||||
|
strikethrough?: boolean;
|
||||||
matches?: IMatch[];
|
matches?: IMatch[];
|
||||||
labelEscapeNewLines?: boolean;
|
labelEscapeNewLines?: boolean;
|
||||||
descriptionMatches?: IMatch[];
|
descriptionMatches?: IMatch[];
|
||||||
@@ -136,6 +137,10 @@ export class IconLabel extends Disposable {
|
|||||||
if (options.italic) {
|
if (options.italic) {
|
||||||
classes.push('italic');
|
classes.push('italic');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.strikethrough) {
|
||||||
|
classes.push('strikethrough');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.domNode.className = classes.join(' ');
|
this.domNode.className = classes.join(' ');
|
||||||
|
|||||||
@@ -60,6 +60,11 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name,
|
||||||
|
.monaco-icon-label.strikethrough > .monaco-icon-description-container > .label-description {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
.monaco-icon-label::after {
|
.monaco-icon-label::after {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
|||||||
@@ -319,6 +319,10 @@ export class InputBox extends Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSelectionAtEnd(): boolean {
|
||||||
|
return this.input.selectionEnd === this.input.value.length && this.input.selectionStart === this.input.selectionEnd;
|
||||||
|
}
|
||||||
|
|
||||||
public enable(): void {
|
public enable(): void {
|
||||||
this.input.removeAttribute('disabled');
|
this.input.removeAttribute('disabled');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export interface IIdentityProvider<T> {
|
|||||||
|
|
||||||
export enum ListAriaRootRole {
|
export enum ListAriaRootRole {
|
||||||
/** default list structure role */
|
/** default list structure role */
|
||||||
LIST = 'list',
|
LIST = 'listbox',
|
||||||
|
|
||||||
/** default tree structure role */
|
/** default tree structure role */
|
||||||
TREE = 'tree',
|
TREE = 'tree',
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ export abstract class Pane extends Disposable implements IView {
|
|||||||
|
|
||||||
this.body = append(this.element, $('.pane-body'));
|
this.body = append(this.element, $('.pane-body'));
|
||||||
this.renderBody(this.body);
|
this.renderBody(this.body);
|
||||||
|
|
||||||
|
if (!this.isExpanded()) {
|
||||||
|
this.body.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(size: number): void {
|
layout(size: number): void {
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ export function anyScore(pattern: string, lowPattern: string, _patternPos: numbe
|
|||||||
|
|
||||||
//#region --- fuzzyScore ---
|
//#region --- fuzzyScore ---
|
||||||
|
|
||||||
export function createMatches(score: undefined | FuzzyScore): IMatch[] {
|
export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch[] {
|
||||||
if (typeof score === 'undefined') {
|
if (typeof score === 'undefined') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -407,7 +407,7 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] {
|
|||||||
if (last && last.end === pos) {
|
if (last && last.end === pos) {
|
||||||
last.end = pos + 1;
|
last.end = pos + 1;
|
||||||
} else {
|
} else {
|
||||||
res.push({ start: pos, end: pos + 1 });
|
res.push({ start: pos + offset, end: pos + 1 + offset });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|||||||
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
||||||
* and encoding.
|
* and encoding.
|
||||||
*
|
*
|
||||||
|
* ```txt
|
||||||
* foo://example.com:8042/over/there?name=ferret#nose
|
* foo://example.com:8042/over/there?name=ferret#nose
|
||||||
* \_/ \______________/\_________/ \_________/ \__/
|
* \_/ \______________/\_________/ \_________/ \__/
|
||||||
* | | | | |
|
* | | | | |
|
||||||
@@ -92,6 +93,7 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|||||||
* | _____________________|__
|
* | _____________________|__
|
||||||
* / \ / \
|
* / \ / \
|
||||||
* urn:example:animal:ferret:nose
|
* urn:example:animal:ferret:nose
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
export class URI implements UriComponents {
|
export class URI implements UriComponents {
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import 'vs/css!./media/quickInput';
|
import 'vs/css!./media/quickInput';
|
||||||
import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/base/parts/quickinput/common/quickInput';
|
import { IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
|
||||||
import * as dom from 'vs/base/browser/dom';
|
import * as dom from 'vs/base/browser/dom';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { QuickInputList } from './quickInputList';
|
import { QuickInputList } from './quickInputList';
|
||||||
@@ -379,11 +379,12 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
private _ariaLabel = QuickPick.DEFAULT_ARIA_LABEL;
|
private _ariaLabel = QuickPick.DEFAULT_ARIA_LABEL;
|
||||||
private _placeholder: string | undefined;
|
private _placeholder: string | undefined;
|
||||||
private readonly onDidChangeValueEmitter = this._register(new Emitter<string>());
|
private readonly onDidChangeValueEmitter = this._register(new Emitter<string>());
|
||||||
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
|
private readonly onDidAcceptEmitter = this._register(new Emitter<IQuickPickAcceptEvent>());
|
||||||
private readonly onDidCustomEmitter = this._register(new Emitter<void>());
|
private readonly onDidCustomEmitter = this._register(new Emitter<void>());
|
||||||
private _items: Array<T | IQuickPickSeparator> = [];
|
private _items: Array<T | IQuickPickSeparator> = [];
|
||||||
private itemsUpdated = false;
|
private itemsUpdated = false;
|
||||||
private _canSelectMany = false;
|
private _canSelectMany = false;
|
||||||
|
private _canAcceptInBackground = false;
|
||||||
private _matchOnDescription = false;
|
private _matchOnDescription = false;
|
||||||
private _matchOnDetail = false;
|
private _matchOnDetail = false;
|
||||||
private _matchOnLabel = true;
|
private _matchOnLabel = true;
|
||||||
@@ -462,6 +463,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canAcceptInBackground() {
|
||||||
|
return this._canAcceptInBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
set canAcceptInBackground(canAcceptInBackground: boolean) {
|
||||||
|
this._canAcceptInBackground = canAcceptInBackground;
|
||||||
|
}
|
||||||
|
|
||||||
get matchOnDescription() {
|
get matchOnDescription() {
|
||||||
return this._matchOnDescription;
|
return this._matchOnDescription;
|
||||||
}
|
}
|
||||||
@@ -663,6 +672,22 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
this.ui.list.domFocus();
|
this.ui.list.domFocus();
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
break;
|
||||||
|
case KeyCode.RightArrow:
|
||||||
|
if (!this._canAcceptInBackground) {
|
||||||
|
return; // needs to be enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.ui.inputBox.isSelectionAtEnd()) {
|
||||||
|
return; // ensure input box selection at end
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.activeItems[0]) {
|
||||||
|
this._selectedItems = [this.activeItems[0]];
|
||||||
|
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
||||||
|
this.onDidAcceptEmitter.fire({ inBackground: true });
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -671,7 +696,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
this._selectedItems = [this.activeItems[0]];
|
this._selectedItems = [this.activeItems[0]];
|
||||||
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
||||||
}
|
}
|
||||||
this.onDidAcceptEmitter.fire(undefined);
|
this.onDidAcceptEmitter.fire({ inBackground: false });
|
||||||
}));
|
}));
|
||||||
this.visibleDisposables.add(this.ui.onDidCustom(() => {
|
this.visibleDisposables.add(this.ui.onDidCustom(() => {
|
||||||
this.onDidCustomEmitter.fire(undefined);
|
this.onDidCustomEmitter.fire(undefined);
|
||||||
@@ -686,7 +711,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
this._activeItems = focusedItems as T[];
|
this._activeItems = focusedItems as T[];
|
||||||
this.onDidChangeActiveEmitter.fire(focusedItems as T[]);
|
this.onDidChangeActiveEmitter.fire(focusedItems as T[]);
|
||||||
}));
|
}));
|
||||||
this.visibleDisposables.add(this.ui.list.onDidChangeSelection(selectedItems => {
|
this.visibleDisposables.add(this.ui.list.onDidChangeSelection(({ items: selectedItems, event }) => {
|
||||||
if (this.canSelectMany) {
|
if (this.canSelectMany) {
|
||||||
if (selectedItems.length) {
|
if (selectedItems.length) {
|
||||||
this.ui.list.setSelectedElements([]);
|
this.ui.list.setSelectedElements([]);
|
||||||
@@ -699,7 +724,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
this._selectedItems = selectedItems as T[];
|
this._selectedItems = selectedItems as T[];
|
||||||
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
|
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
|
||||||
if (selectedItems.length) {
|
if (selectedItems.length) {
|
||||||
this.onDidAcceptEmitter.fire(undefined);
|
this.onDidAcceptEmitter.fire({ inBackground: event instanceof MouseEvent && event.button === 1 /* mouse middle click */ });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => {
|
this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => {
|
||||||
@@ -762,7 +787,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||||||
if (wasTriggerKeyPressed && this.activeItems[0]) {
|
if (wasTriggerKeyPressed && this.activeItems[0]) {
|
||||||
this._selectedItems = [this.activeItems[0]];
|
this._selectedItems = [this.activeItems[0]];
|
||||||
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
|
||||||
this.onDidAcceptEmitter.fire(undefined);
|
this.onDidAcceptEmitter.fire({ inBackground: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ export class QuickInputBox extends Disposable {
|
|||||||
this.inputBox.select(range);
|
this.inputBox.select(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlaceholder(placeholder: string) {
|
isSelectionAtEnd(): boolean {
|
||||||
|
return this.inputBox.isSelectionAtEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlaceholder(placeholder: string): void {
|
||||||
this.inputBox.setPlaceHolder(placeholder);
|
this.inputBox.setPlaceHolder(placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +94,11 @@ export class QuickInputBox extends Disposable {
|
|||||||
return this.inputBox.hasFocus();
|
return this.inputBox.hasFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
setAttribute(name: string, value: string) {
|
setAttribute(name: string, value: string): void {
|
||||||
this.inputBox.inputElement.setAttribute(name, value);
|
this.inputBox.inputElement.setAttribute(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAttribute(name: string) {
|
removeAttribute(name: string): void {
|
||||||
this.inputBox.inputElement.removeAttribute(name);
|
this.inputBox.inputElement.removeAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +122,7 @@ export class QuickInputBox extends Disposable {
|
|||||||
this.inputBox.layout();
|
this.inputBox.layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
style(styles: IInputBoxStyles) {
|
style(styles: IInputBoxStyles): void {
|
||||||
this.inputBox.style(styles);
|
this.inputBox.style(styles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
|
|||||||
options.descriptionMatches = descriptionHighlights || [];
|
options.descriptionMatches = descriptionHighlights || [];
|
||||||
options.extraClasses = element.item.iconClasses;
|
options.extraClasses = element.item.iconClasses;
|
||||||
options.italic = element.item.italic;
|
options.italic = element.item.italic;
|
||||||
|
options.strikethrough = element.item.strikethrough;
|
||||||
data.label.setLabel(element.saneLabel, element.saneDescription, options);
|
data.label.setLabel(element.saneLabel, element.saneDescription, options);
|
||||||
|
|
||||||
// Keybinding
|
// Keybinding
|
||||||
@@ -317,7 +318,7 @@ export class QuickInputList {
|
|||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
get onDidChangeSelection() {
|
get onDidChangeSelection() {
|
||||||
return Event.map(this.list.onDidChangeSelection, e => e.elements.map(e => e.item));
|
return Event.map(this.list.onDidChangeSelection, e => ({ items: e.elements.map(e => e.item), event: e.browserEvent }));
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllVisibleChecked() {
|
getAllVisibleChecked() {
|
||||||
|
|||||||
@@ -24,9 +24,15 @@ export interface IQuickPickItem {
|
|||||||
ariaLabel?: string;
|
ariaLabel?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
detail?: string;
|
detail?: string;
|
||||||
|
/**
|
||||||
|
* Allows to show a keybinding next to the item to indicate
|
||||||
|
* how the item can be triggered outside of the picker using
|
||||||
|
* keyboard shortcut.
|
||||||
|
*/
|
||||||
keybinding?: ResolvedKeybinding;
|
keybinding?: ResolvedKeybinding;
|
||||||
iconClasses?: string[];
|
iconClasses?: string[];
|
||||||
italic?: boolean;
|
italic?: boolean;
|
||||||
|
strikethrough?: boolean;
|
||||||
highlights?: IQuickPickItemHighlights;
|
highlights?: IQuickPickItemHighlights;
|
||||||
buttons?: IQuickInputButton[];
|
buttons?: IQuickInputButton[];
|
||||||
/**
|
/**
|
||||||
@@ -170,6 +176,15 @@ export interface IQuickInput extends IDisposable {
|
|||||||
hide(): void;
|
hide(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IQuickPickAcceptEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals if the picker item is to be accepted
|
||||||
|
* in the background while keeping the picker open.
|
||||||
|
*/
|
||||||
|
inBackground: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
||||||
|
|
||||||
value: string;
|
value: string;
|
||||||
@@ -186,7 +201,14 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
|||||||
|
|
||||||
readonly onDidChangeValue: Event<string>;
|
readonly onDidChangeValue: Event<string>;
|
||||||
|
|
||||||
readonly onDidAccept: Event<void>;
|
readonly onDidAccept: Event<IQuickPickAcceptEvent>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, will fire the `onDidAccept` event when
|
||||||
|
* pressing the arrow-right key with the idea of accepting
|
||||||
|
* the selected item without closing the picker.
|
||||||
|
*/
|
||||||
|
canAcceptInBackground: boolean;
|
||||||
|
|
||||||
ok: boolean | 'default';
|
ok: boolean | 'default';
|
||||||
|
|
||||||
@@ -268,7 +290,6 @@ export interface IQuickInputButton {
|
|||||||
/** iconPath or iconClass required */
|
/** iconPath or iconClass required */
|
||||||
iconClass?: string;
|
iconClass?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
alwaysShow?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuickPickItemButtonEvent<T extends IQuickPickItem> {
|
export interface IQuickPickItemButtonEvent<T extends IQuickPickItem> {
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ export class HeightMap {
|
|||||||
totalSize = viewItem.top + viewItem.height;
|
totalSize = viewItem.top + viewItem.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
let boundSplice = this.heightMap.splice.bind(this.heightMap, i, 0);
|
const startingIndex = i;
|
||||||
|
|
||||||
let itemsToInsert: IViewItem[] = [];
|
let itemsToInsert: IViewItem[] = [];
|
||||||
|
|
||||||
while (item = iterator.next()) {
|
while (item = iterator.next()) {
|
||||||
@@ -58,7 +57,7 @@ export class HeightMap {
|
|||||||
sizeDiff += viewItem.height;
|
sizeDiff += viewItem.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
boundSplice.apply(this.heightMap, itemsToInsert);
|
this.heightMap.splice(startingIndex, 0, ...itemsToInsert);
|
||||||
|
|
||||||
for (j = i; j < this.heightMap.length; j++) {
|
for (j = i; j < this.heightMap.length; j++) {
|
||||||
viewItem = this.heightMap[j];
|
viewItem = this.heightMap[j];
|
||||||
|
|||||||
@@ -49,17 +49,16 @@ import { IFileService } from 'vs/platform/files/common/files';
|
|||||||
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
|
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import { IProductService } from 'vs/platform/product/common/productService';
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
||||||
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
|
||||||
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
|
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
|
||||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||||
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
||||||
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
|
||||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||||
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
|
import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService';
|
||||||
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/userDataAutoSyncService';
|
import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/userDataAutoSyncService';
|
||||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
|
||||||
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
|
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
|
||||||
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
|
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
@@ -197,7 +196,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
|||||||
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
|
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
|
||||||
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
|
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
|
||||||
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
|
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
|
||||||
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
|
|
||||||
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
|
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
|
||||||
registerConfiguration();
|
registerConfiguration();
|
||||||
|
|
||||||
@@ -229,10 +227,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
|
|||||||
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
|
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
|
||||||
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
|
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
|
||||||
|
|
||||||
const settingsSyncService = accessor.get(ISettingsSyncService);
|
|
||||||
const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService);
|
|
||||||
server.registerChannel('settingsSync', settingsSyncChannel);
|
|
||||||
|
|
||||||
const userDataSyncService = accessor.get(IUserDataSyncService);
|
const userDataSyncService = accessor.get(IUserDataSyncService);
|
||||||
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
|
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
|
||||||
server.registerChannel('userDataSync', userDataSyncChannel);
|
server.registerChannel('userDataSync', userDataSyncChannel);
|
||||||
|
|||||||
@@ -28,12 +28,13 @@ import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/commo
|
|||||||
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
||||||
import { endsWith } from 'vs/base/common/strings';
|
import { endsWith } from 'vs/base/common/strings';
|
||||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||||
import { IFileService } from 'vs/platform/files/common/files';
|
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
||||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||||
|
import { IStorageMainService } from 'vs/platform/storage/node/storageMainService';
|
||||||
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
|
|
||||||
const RUN_TEXTMATE_IN_WORKER = false;
|
const RUN_TEXTMATE_IN_WORKER = false;
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
@ILogService private readonly logService: ILogService,
|
@ILogService private readonly logService: ILogService,
|
||||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||||
@IFileService private readonly fileService: IFileService,
|
@IFileService private readonly fileService: IFileService,
|
||||||
|
@IStorageMainService private readonly storageService: IStorageMainService,
|
||||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||||
@IThemeMainService private readonly themeMainService: IThemeMainService,
|
@IThemeMainService private readonly themeMainService: IThemeMainService,
|
||||||
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
@IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService,
|
||||||
@@ -226,7 +228,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
this.createTouchBar();
|
this.createTouchBar();
|
||||||
|
|
||||||
// Request handling
|
// Request handling
|
||||||
this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService);
|
const that = this;
|
||||||
|
this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService, {
|
||||||
|
get(key) { return that.storageService.get(key); },
|
||||||
|
store(key, value) { that.storageService.store(key, value); }
|
||||||
|
});
|
||||||
|
|
||||||
// Eventing
|
// Eventing
|
||||||
this.registerListeners();
|
this.registerListeners();
|
||||||
|
|||||||
@@ -1754,6 +1754,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
if (ranges.length > 0) {
|
if (ranges.length > 0) {
|
||||||
this._emitModelTokensChangedEvent({
|
this._emitModelTokensChangedEvent({
|
||||||
tokenizationSupportChanged: false,
|
tokenizationSupportChanged: false,
|
||||||
|
semanticTokensApplied: false,
|
||||||
ranges: ranges
|
ranges: ranges
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1764,6 +1765,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
|
|
||||||
this._emitModelTokensChangedEvent({
|
this._emitModelTokensChangedEvent({
|
||||||
tokenizationSupportChanged: false,
|
tokenizationSupportChanged: false,
|
||||||
|
semanticTokensApplied: tokens !== null,
|
||||||
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
|
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1778,6 +1780,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
this._tokens.flush();
|
this._tokens.flush();
|
||||||
this._emitModelTokensChangedEvent({
|
this._emitModelTokensChangedEvent({
|
||||||
tokenizationSupportChanged: true,
|
tokenizationSupportChanged: true,
|
||||||
|
semanticTokensApplied: false,
|
||||||
ranges: [{
|
ranges: [{
|
||||||
fromLineNumber: 1,
|
fromLineNumber: 1,
|
||||||
toLineNumber: this._buffer.getLineCount()
|
toLineNumber: this._buffer.getLineCount()
|
||||||
@@ -1790,6 +1793,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
|
|
||||||
this._emitModelTokensChangedEvent({
|
this._emitModelTokensChangedEvent({
|
||||||
tokenizationSupportChanged: false,
|
tokenizationSupportChanged: false,
|
||||||
|
semanticTokensApplied: false,
|
||||||
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
|
ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export interface IModelDecorationsChangedEvent {
|
|||||||
*/
|
*/
|
||||||
export interface IModelTokensChangedEvent {
|
export interface IModelTokensChangedEvent {
|
||||||
readonly tokenizationSupportChanged: boolean;
|
readonly tokenizationSupportChanged: boolean;
|
||||||
|
readonly semanticTokensApplied: boolean;
|
||||||
readonly ranges: {
|
readonly ranges: {
|
||||||
/**
|
/**
|
||||||
* The start of the range (inclusive)
|
* The start of the range (inclusive)
|
||||||
|
|||||||
@@ -466,15 +466,16 @@ class SemanticColoringFeature extends Disposable {
|
|||||||
|
|
||||||
private _watchers: Record<string, ModelSemanticColoring>;
|
private _watchers: Record<string, ModelSemanticColoring>;
|
||||||
private _semanticStyling: SemanticStyling;
|
private _semanticStyling: SemanticStyling;
|
||||||
private _configurationService: IConfigurationService;
|
|
||||||
|
|
||||||
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
|
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
|
||||||
super();
|
super();
|
||||||
this._configurationService = configurationService;
|
|
||||||
this._watchers = Object.create(null);
|
this._watchers = Object.create(null);
|
||||||
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
|
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
|
||||||
|
|
||||||
const isSemanticColoringEnabled = (model: ITextModel) => {
|
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 });
|
const options = configurationService.getValue<IEditorSemanticHighlightingOptions>(SemanticColoringFeature.SETTING_ID, { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri });
|
||||||
return options && options.enabled;
|
return options && options.enabled;
|
||||||
};
|
};
|
||||||
@@ -485,6 +486,20 @@ class SemanticColoringFeature extends Disposable {
|
|||||||
modelSemanticColoring.dispose();
|
modelSemanticColoring.dispose();
|
||||||
delete this._watchers[model.uri.toString()];
|
delete this._watchers[model.uri.toString()];
|
||||||
};
|
};
|
||||||
|
const handleSettingOrThemeChange = () => {
|
||||||
|
for (let model of modelService.getModels()) {
|
||||||
|
const curr = this._watchers[model.uri.toString()];
|
||||||
|
if (isSemanticColoringEnabled(model)) {
|
||||||
|
if (!curr) {
|
||||||
|
register(model);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (curr) {
|
||||||
|
deregister(model, curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
this._register(modelService.onModelAdded((model) => {
|
this._register(modelService.onModelAdded((model) => {
|
||||||
if (isSemanticColoringEnabled(model)) {
|
if (isSemanticColoringEnabled(model)) {
|
||||||
register(model);
|
register(model);
|
||||||
@@ -496,22 +511,12 @@ class SemanticColoringFeature extends Disposable {
|
|||||||
deregister(model, curr);
|
deregister(model, curr);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._configurationService.onDidChangeConfiguration(e => {
|
this._register(configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
|
if (e.affectsConfiguration(SemanticColoringFeature.SETTING_ID)) {
|
||||||
for (let model of modelService.getModels()) {
|
handleSettingOrThemeChange();
|
||||||
const curr = this._watchers[model.uri.toString()];
|
|
||||||
if (isSemanticColoringEnabled(model)) {
|
|
||||||
if (!curr) {
|
|
||||||
register(model);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (curr) {
|
|
||||||
deregister(model, curr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
this._register(themeService.onDidColorThemeChange(handleSettingOrThemeChange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,12 +530,9 @@ class SemanticStyling extends Disposable {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
||||||
if (this._themeService) {
|
this._register(this._themeService.onDidColorThemeChange(() => {
|
||||||
// workaround for tests which use undefined... :/
|
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
||||||
this._register(this._themeService.onDidColorThemeChange(() => {
|
}));
|
||||||
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling {
|
public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling {
|
||||||
@@ -768,14 +770,12 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
this._fetchSemanticTokens.schedule();
|
this._fetchSemanticTokens.schedule();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (themeService) {
|
this._register(themeService.onDidColorThemeChange(_ => {
|
||||||
// workaround for tests which use undefined... :/
|
// clear out existing tokens
|
||||||
this._register(themeService.onDidColorThemeChange(_ => {
|
this._setSemanticTokens(null, null, null, []);
|
||||||
// clear out existing tokens
|
this._fetchSemanticTokens.schedule();
|
||||||
this._setSemanticTokens(null, null, null, []);
|
}));
|
||||||
this._fetchSemanticTokens.schedule();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this._fetchSemanticTokens.schedule(0);
|
this._fetchSemanticTokens.schedule(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,6 @@ export namespace InspectTokensNLS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace GoToLineNLS {
|
export namespace GoToLineNLS {
|
||||||
export const gotoLineLabelValidLineAndColumn = nls.localize('gotoLineLabelValidLineAndColumn', "Go to line {0} and character {1}");
|
|
||||||
export const gotoLineLabelValidLine = nls.localize('gotoLineLabelValidLine', "Go to line {0}");
|
|
||||||
export const gotoLineLabelEmptyWithLineLimit = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to");
|
|
||||||
export const gotoLineLabelEmptyWithLineAndColumnLimit = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to");
|
|
||||||
export const gotoLineAriaLabel = nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {1}.");
|
|
||||||
export const gotoLineActionInput = nls.localize('gotoLineActionInput', "Type a line number, followed by an optional colon and a character number to navigate to");
|
|
||||||
export const gotoLineActionLabel = nls.localize('gotoLineActionLabel', "Go to Line...");
|
export const gotoLineActionLabel = nls.localize('gotoLineActionLabel', "Go to Line...");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,28 +44,13 @@ export namespace QuickHelpNLS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace QuickCommandNLS {
|
export namespace QuickCommandNLS {
|
||||||
export const ariaLabelEntryWithKey = nls.localize('ariaLabelEntryWithKey', "{0}, {1}, commands");
|
|
||||||
export const ariaLabelEntry = nls.localize('ariaLabelEntry', "{0}, commands");
|
|
||||||
export const quickCommandActionInput = nls.localize('quickCommandActionInput', "Type the name of an action you want to execute");
|
|
||||||
export const quickCommandActionLabel = nls.localize('quickCommandActionLabel', "Command Palette");
|
export const quickCommandActionLabel = nls.localize('quickCommandActionLabel', "Command Palette");
|
||||||
export const quickCommandHelp = nls.localize('quickCommandActionHelp', "Show And Run Commands");
|
export const quickCommandHelp = nls.localize('quickCommandActionHelp', "Show And Run Commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace QuickOutlineNLS {
|
export namespace QuickOutlineNLS {
|
||||||
export const entryAriaLabel = nls.localize('entryAriaLabel', "{0}, symbols");
|
|
||||||
export const quickOutlineActionInput = nls.localize('quickOutlineActionInput', "Type the name of an identifier you wish to navigate to");
|
|
||||||
export const quickOutlineActionLabel = nls.localize('quickOutlineActionLabel', "Go to Symbol...");
|
export const quickOutlineActionLabel = nls.localize('quickOutlineActionLabel', "Go to Symbol...");
|
||||||
export const _symbols_ = nls.localize('symbols', "symbols ({0})");
|
export const quickOutlineByCategoryActionLabel = nls.localize('quickOutlineByCategoryActionLabel', "Go to Symbol by Category...");
|
||||||
export const _modules_ = nls.localize('modules', "modules ({0})");
|
|
||||||
export const _class_ = nls.localize('class', "classes ({0})");
|
|
||||||
export const _interface_ = nls.localize('interface', "interfaces ({0})");
|
|
||||||
export const _method_ = nls.localize('method', "methods ({0})");
|
|
||||||
export const _function_ = nls.localize('function', "functions ({0})");
|
|
||||||
export const _property_ = nls.localize('property', "properties ({0})");
|
|
||||||
export const _variable_ = nls.localize('variable', "variables ({0})");
|
|
||||||
export const _variable2_ = nls.localize('variable2', "variables ({0})");
|
|
||||||
export const _constructor_ = nls.localize('_constructor', "constructors ({0})");
|
|
||||||
export const _call_ = nls.localize('call', "calls ({0})");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace StandaloneCodeEditorNLS {
|
export namespace StandaloneCodeEditorNLS {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export abstract class AbstractEditorCommandsQuickAccessProvider extends Abstract
|
|||||||
/**
|
/**
|
||||||
* Subclasses to provide the current active editor control.
|
* Subclasses to provide the current active editor control.
|
||||||
*/
|
*/
|
||||||
abstract activeTextEditorControl: IEditor | undefined;
|
protected abstract activeTextEditorControl: IEditor | undefined;
|
||||||
|
|
||||||
protected getCodeEditorCommandPicks(): ICommandQuickPick[] {
|
protected getCodeEditorCommandPicks(): ICommandQuickPick[] {
|
||||||
const activeTextEditorControl = this.activeTextEditorControl;
|
const activeTextEditorControl = this.activeTextEditorControl;
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
||||||
|
import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
|
||||||
|
import { IModelDeltaDecoration, OverviewRulerLane, ITextModel } from 'vs/editor/common/model';
|
||||||
|
import { IRange } from 'vs/editor/common/core/range';
|
||||||
|
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
||||||
|
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
|
||||||
|
import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
|
import { IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
|
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
|
import { once } from 'vs/base/common/functional';
|
||||||
|
|
||||||
|
interface IEditorLineDecoration {
|
||||||
|
rangeHighlightId: string;
|
||||||
|
overviewRulerDecorationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reusable quick access provider for the editor with support
|
||||||
|
* for adding decorations for navigating in the currently active file
|
||||||
|
* (for example "Go to line", "Go to symbol").
|
||||||
|
*/
|
||||||
|
export abstract class AbstractEditorNavigationQuickAccessProvider implements IQuickAccessProvider {
|
||||||
|
|
||||||
|
//#region Provider methods
|
||||||
|
|
||||||
|
provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
|
// Disable filtering & sorting, we control the results
|
||||||
|
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||||
|
|
||||||
|
// Provide based on current active editor
|
||||||
|
let pickerDisposable = this.doProvide(picker, token);
|
||||||
|
disposables.add(toDisposable(() => pickerDisposable.dispose()));
|
||||||
|
|
||||||
|
// Re-create whenever the active editor changes
|
||||||
|
disposables.add(this.onDidActiveTextEditorControlChange(() => {
|
||||||
|
pickerDisposable.dispose();
|
||||||
|
pickerDisposable = this.doProvide(picker, token);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private doProvide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
|
// With text control
|
||||||
|
const editor = this.activeTextEditorControl;
|
||||||
|
if (editor && this.canProvideWithTextEditor(editor)) {
|
||||||
|
|
||||||
|
// Restore any view state if this picker was closed
|
||||||
|
// without actually going to a line
|
||||||
|
const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
|
||||||
|
once(token.onCancellationRequested)(() => {
|
||||||
|
if (lastKnownEditorViewState) {
|
||||||
|
editor.restoreViewState(lastKnownEditorViewState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clean up decorations on dispose
|
||||||
|
disposables.add(toDisposable(() => this.clearDecorations(editor)));
|
||||||
|
|
||||||
|
// Ask subclass for entries
|
||||||
|
disposables.add(this.provideWithTextEditor(editor, picker, token));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Without text control
|
||||||
|
else {
|
||||||
|
disposables.add(this.provideWithoutTextEditor(picker, token));
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to implement if they can operate on the text editor.
|
||||||
|
*/
|
||||||
|
protected canProvideWithTextEditor(editor: IEditor): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to implement to provide picks for the picker when an editor is active.
|
||||||
|
*/
|
||||||
|
protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to implement to provide picks for the picker when no editor is active.
|
||||||
|
*/
|
||||||
|
protected abstract provideWithoutTextEditor(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
|
||||||
|
|
||||||
|
protected gotoLocation(editor: IEditor, range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean): void {
|
||||||
|
editor.setSelection(range);
|
||||||
|
editor.revealRangeInCenter(range, ScrollType.Smooth);
|
||||||
|
editor.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
|
||||||
|
return isDiffEditor(editor) ?
|
||||||
|
editor.getModel()?.modified :
|
||||||
|
editor.getModel() as ITextModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
//#region Editor access
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to provide an event when the active editor control changes.
|
||||||
|
*/
|
||||||
|
protected abstract readonly onDidActiveTextEditorControlChange: Event<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to provide the current active editor control.
|
||||||
|
*/
|
||||||
|
protected abstract activeTextEditorControl: IEditor | undefined;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
//#region Decorations Utils
|
||||||
|
|
||||||
|
private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
|
||||||
|
|
||||||
|
protected addDecorations(editor: IEditor, range: IRange): void {
|
||||||
|
editor.changeDecorations(changeAccessor => {
|
||||||
|
|
||||||
|
// Reset old decorations if any
|
||||||
|
const deleteDecorations: string[] = [];
|
||||||
|
if (this.rangeHighlightDecorationId) {
|
||||||
|
deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
|
||||||
|
deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
|
||||||
|
|
||||||
|
this.rangeHighlightDecorationId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new decorations for the range
|
||||||
|
const newDecorations: IModelDeltaDecoration[] = [
|
||||||
|
|
||||||
|
// highlight the entire line on the range
|
||||||
|
{
|
||||||
|
range,
|
||||||
|
options: {
|
||||||
|
className: 'rangeHighlight',
|
||||||
|
isWholeLine: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// also add overview ruler highlight
|
||||||
|
{
|
||||||
|
range,
|
||||||
|
options: {
|
||||||
|
overviewRuler: {
|
||||||
|
color: themeColorFromId(overviewRulerRangeHighlight),
|
||||||
|
position: OverviewRulerLane.Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
|
||||||
|
|
||||||
|
this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected clearDecorations(editor: IEditor): void {
|
||||||
|
const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
|
||||||
|
if (rangeHighlightDecorationId) {
|
||||||
|
editor.changeDecorations(changeAccessor => {
|
||||||
|
changeAccessor.deltaDecorations([
|
||||||
|
rangeHighlightDecorationId.overviewRulerDecorationId,
|
||||||
|
rangeHighlightDecorationId.rangeHighlightId
|
||||||
|
], []);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rangeHighlightDecorationId = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
@@ -4,74 +4,32 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { DisposableStore, toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { once } from 'vs/base/common/functional';
|
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
|
||||||
import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
|
|
||||||
import { ITextModel } from 'vs/editor/common/model';
|
|
||||||
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { IRange } from 'vs/editor/common/core/range';
|
import { IRange } from 'vs/editor/common/core/range';
|
||||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
|
||||||
import { AbstractEditorQuickAccessProvider } from 'vs/editor/contrib/quickAccess/quickAccess';
|
|
||||||
import { IPosition } from 'vs/editor/common/core/position';
|
import { IPosition } from 'vs/editor/common/core/position';
|
||||||
|
|
||||||
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial<IPosition> { }
|
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial<IPosition> { }
|
||||||
|
|
||||||
export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorQuickAccessProvider {
|
export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
|
||||||
|
|
||||||
static PREFIX = ':';
|
static PREFIX = ':';
|
||||||
|
|
||||||
provide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
protected provideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
|
||||||
const disposables = new DisposableStore();
|
const label = localize('cannotRunGotoLine', "Open a text editor first to go to a line.");
|
||||||
|
|
||||||
// Disable filtering & sorting, we control the results
|
|
||||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
|
||||||
|
|
||||||
// Provide based on current active editor
|
|
||||||
let pickerDisposable = this.doProvide(picker, token);
|
|
||||||
disposables.add(toDisposable(() => pickerDisposable.dispose()));
|
|
||||||
|
|
||||||
// Re-create whenever the active editor changes
|
|
||||||
disposables.add(this.onDidActiveTextEditorControlChange(() => {
|
|
||||||
pickerDisposable.dispose();
|
|
||||||
pickerDisposable = this.doProvide(picker, token);
|
|
||||||
}));
|
|
||||||
|
|
||||||
return disposables;
|
|
||||||
}
|
|
||||||
|
|
||||||
private doProvide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
|
||||||
|
|
||||||
// With text control
|
|
||||||
if (this.activeTextEditorControl) {
|
|
||||||
return this.doProvideWithTextEditor(this.activeTextEditorControl, picker, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Without text control
|
|
||||||
return this.doProvideWithoutTextEditor(picker);
|
|
||||||
}
|
|
||||||
|
|
||||||
private doProvideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
|
|
||||||
const label = localize('cannotRunGotoLine', "Open a text file first to go to a line.");
|
|
||||||
picker.items = [{ label }];
|
picker.items = [{ label }];
|
||||||
picker.ariaLabel = label;
|
picker.ariaLabel = label;
|
||||||
|
|
||||||
return Disposable.None;
|
return Disposable.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private doProvideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
protected provideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
const disposables = new DisposableStore();
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
// Restore any view state if this picker was closed
|
|
||||||
// without actually going to a line
|
|
||||||
const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
|
|
||||||
once(token.onCancellationRequested)(() => {
|
|
||||||
if (lastKnownEditorViewState) {
|
|
||||||
editor.restoreViewState(lastKnownEditorViewState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Goto line once picked
|
// Goto line once picked
|
||||||
disposables.add(picker.onDidAccept(() => {
|
disposables.add(picker.onDidAccept(() => {
|
||||||
const [item] = picker.selectedItems;
|
const [item] = picker.selectedItems;
|
||||||
@@ -80,7 +38,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.gotoLine(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
|
this.gotoLocation(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
|
||||||
|
|
||||||
picker.hide();
|
picker.hide();
|
||||||
}
|
}
|
||||||
@@ -117,9 +75,6 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
|||||||
updatePickerAndEditor();
|
updatePickerAndEditor();
|
||||||
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
|
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
|
||||||
|
|
||||||
// Clean up decorations on dispose
|
|
||||||
disposables.add(toDisposable(() => this.clearDecorations(editor)));
|
|
||||||
|
|
||||||
return disposables;
|
return disposables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,16 +146,4 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
|||||||
private lineCount(editor: IEditor): number {
|
private lineCount(editor: IEditor): number {
|
||||||
return this.getModel(editor)?.getLineCount() ?? 0;
|
return this.getModel(editor)?.getLineCount() ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
|
|
||||||
return isDiffEditor(editor) ?
|
|
||||||
editor.getModel()?.modified :
|
|
||||||
editor.getModel() as ITextModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected gotoLine(editor: IEditor, range: IRange, keyMods: IKeyMods): void {
|
|
||||||
editor.setSelection(range);
|
|
||||||
editor.revealRangeInCenter(range, ScrollType.Smooth);
|
|
||||||
editor.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,420 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||||
|
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
|
||||||
|
import { ITextModel } from 'vs/editor/common/model';
|
||||||
|
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||||
|
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
|
||||||
|
import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes';
|
||||||
|
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';
|
||||||
|
|
||||||
|
interface IGotoSymbolQuickPickItem extends IQuickPickItem {
|
||||||
|
kind: SymbolKind,
|
||||||
|
index: number,
|
||||||
|
score?: FuzzyScore;
|
||||||
|
range?: { decoration: IRange, selection: IRange },
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGotoSymbolQuickAccessProviderOptions {
|
||||||
|
openSideBySideDirection: () => undefined | 'right' | 'down'
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
|
||||||
|
|
||||||
|
static PREFIX = '@';
|
||||||
|
static SCOPE_PREFIX = ':';
|
||||||
|
static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
|
||||||
|
|
||||||
|
constructor(private options?: IGotoSymbolQuickAccessProviderOptions) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
||||||
|
const label = localize('cannotRunGotoSymbolWithoutEditor', "Open a text editor first to go to a symbol.");
|
||||||
|
|
||||||
|
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
||||||
|
picker.ariaLabel = label;
|
||||||
|
|
||||||
|
return Disposable.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected provideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
|
const model = this.getModel(editor);
|
||||||
|
if (!model) {
|
||||||
|
return Disposable.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide symbols from model if available in registry
|
||||||
|
if (DocumentSymbolProviderRegistry.has(model)) {
|
||||||
|
return this.doProvideWithEditorSymbols(editor, model, picker, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise show an entry for a model without registry
|
||||||
|
// But give a chance to resolve the symbols at a later
|
||||||
|
// point if possible
|
||||||
|
return this.doProvideWithoutEditorSymbols(editor, model, picker, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
|
// Generic pick for not having any symbol information
|
||||||
|
const label = localize('cannotRunGotoSymbolWithoutSymbolProvider', "Open a text editor with symbol information first to go to a symbol.");
|
||||||
|
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
||||||
|
picker.ariaLabel = label;
|
||||||
|
|
||||||
|
// Listen to changes to the registry and see if eventually
|
||||||
|
// we do get symbols. This can happen if the picker is opened
|
||||||
|
// very early after the model has loaded but before the
|
||||||
|
// language registry is ready.
|
||||||
|
// https://github.com/microsoft/vscode/issues/70607
|
||||||
|
const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => {
|
||||||
|
if (DocumentSymbolProviderRegistry.has(model)) {
|
||||||
|
symbolProviderListener.dispose();
|
||||||
|
|
||||||
|
disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
|
||||||
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
|
// Goto symbol once picked
|
||||||
|
disposables.add(picker.onDidAccept(() => {
|
||||||
|
const [item] = picker.selectedItems;
|
||||||
|
if (item && item.range) {
|
||||||
|
this.gotoLocation(editor, item.range.selection, picker.keyMods);
|
||||||
|
|
||||||
|
picker.hide();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Goto symbol side by side if enabled
|
||||||
|
disposables.add(picker.onDidTriggerItemButton(({ item }) => {
|
||||||
|
if (item && item.range) {
|
||||||
|
this.gotoLocation(editor, item.range.selection, picker.keyMods, true);
|
||||||
|
|
||||||
|
picker.hide();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Resolve symbols from document once and reuse this
|
||||||
|
// request for all filtering and typing then on
|
||||||
|
const symbolsPromise = this.getDocumentSymbols(model, true, token);
|
||||||
|
|
||||||
|
// Set initial picks and update on type
|
||||||
|
let picksCts: CancellationTokenSource | undefined = undefined;
|
||||||
|
const updatePickerItems = async () => {
|
||||||
|
|
||||||
|
// Cancel any previous ask for picks and busy
|
||||||
|
picksCts?.dispose(true);
|
||||||
|
picker.busy = false;
|
||||||
|
|
||||||
|
// Create new cancellation source for this run
|
||||||
|
picksCts = new CancellationTokenSource(token);
|
||||||
|
|
||||||
|
// Collect symbol picks
|
||||||
|
picker.busy = true;
|
||||||
|
try {
|
||||||
|
const items = await this.getSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
picker.items = items;
|
||||||
|
} finally {
|
||||||
|
if (!token.isCancellationRequested) {
|
||||||
|
picker.busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
||||||
|
updatePickerItems();
|
||||||
|
|
||||||
|
// Reveal and decorate when active item changes
|
||||||
|
// However, ignore the very first event so that
|
||||||
|
// opening the picker is not immediately revealing
|
||||||
|
// and decorating the first entry.
|
||||||
|
let ignoreFirstActiveEvent = true;
|
||||||
|
disposables.add(picker.onDidChangeActive(() => {
|
||||||
|
const [item] = picker.activeItems;
|
||||||
|
if (item && item.range) {
|
||||||
|
if (ignoreFirstActiveEvent) {
|
||||||
|
ignoreFirstActiveEvent = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reveal
|
||||||
|
editor.revealRangeInCenter(item.range.selection, ScrollType.Smooth);
|
||||||
|
|
||||||
|
// Decorate
|
||||||
|
this.addDecorations(editor, item.range.decoration);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, filter: string, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
|
||||||
|
const symbols = await symbolsPromise;
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize filter
|
||||||
|
const filterBySymbolKind = filter.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
|
||||||
|
const filterPos = filterBySymbolKind ? 1 : 0;
|
||||||
|
const [symbolFilter, containerFilter] = filter.split(' ') as [string, string | undefined];
|
||||||
|
const symbolFilterLow = symbolFilter.toLowerCase();
|
||||||
|
const containerFilterLow = containerFilter?.toLowerCase();
|
||||||
|
|
||||||
|
// Convert to symbol picks and apply filtering
|
||||||
|
const filteredSymbolPicks: IGotoSymbolQuickPickItem[] = [];
|
||||||
|
for (let index = 0; index < symbols.length; index++) {
|
||||||
|
const symbol = symbols[index];
|
||||||
|
|
||||||
|
const symbolLabel = trim(symbol.name);
|
||||||
|
const containerLabel = symbol.containerName;
|
||||||
|
|
||||||
|
let symbolScore: FuzzyScore | undefined = undefined;
|
||||||
|
let containerScore: FuzzyScore | undefined = undefined;
|
||||||
|
|
||||||
|
let includeSymbol = true;
|
||||||
|
if (filter.length > filterPos) {
|
||||||
|
|
||||||
|
// Score by symbol
|
||||||
|
symbolScore = fuzzyScore(symbolFilter, symbolFilterLow, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
|
||||||
|
includeSymbol = !!symbolScore;
|
||||||
|
|
||||||
|
// Score by container if specified
|
||||||
|
if (includeSymbol && containerFilter && containerFilterLow) {
|
||||||
|
if (containerLabel) {
|
||||||
|
containerScore = fuzzyScore(containerFilter, containerFilterLow, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
includeSymbol = !!containerScore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeSymbol) {
|
||||||
|
const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`;
|
||||||
|
const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
const sortedFilteredSymbolPicks = filteredSymbolPicks.sort((symbolA, symbolB) => filterBySymbolKind ?
|
||||||
|
this.compareByKindAndScore(symbolA, symbolB) :
|
||||||
|
this.compareByScore(symbolA, symbolB)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add separator for types
|
||||||
|
// - @ only total number of symbols
|
||||||
|
// - @: grouped by symbol kind
|
||||||
|
let symbolPicks: Array<IGotoSymbolQuickPickItem | IQuickPickSeparator> = [];
|
||||||
|
if (filterBySymbolKind) {
|
||||||
|
let lastSymbolKind: SymbolKind | undefined = undefined;
|
||||||
|
let lastSeparator: IQuickPickSeparator | undefined = undefined;
|
||||||
|
let lastSymbolKindCounter = 0;
|
||||||
|
|
||||||
|
function updateLastSeparatorLabel(): void {
|
||||||
|
if (lastSeparator && typeof lastSymbolKind === 'number' && lastSymbolKindCounter > 0) {
|
||||||
|
lastSeparator.label = format(NLS_SYMBOL_KIND_CACHE[lastSymbolKind] || FALLBACK_NLS_SYMBOL_KIND, lastSymbolKindCounter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const symbolPick of sortedFilteredSymbolPicks) {
|
||||||
|
|
||||||
|
// Found new kind
|
||||||
|
if (lastSymbolKind !== symbolPick.kind) {
|
||||||
|
|
||||||
|
// Update last separator with number of symbols we found for kind
|
||||||
|
updateLastSeparatorLabel();
|
||||||
|
|
||||||
|
lastSymbolKind = symbolPick.kind;
|
||||||
|
lastSymbolKindCounter = 1;
|
||||||
|
|
||||||
|
// Add new separator for new kind
|
||||||
|
lastSeparator = { type: 'separator' };
|
||||||
|
symbolPicks.push(lastSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing kind, keep counting
|
||||||
|
else {
|
||||||
|
lastSymbolKindCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to final result
|
||||||
|
symbolPicks.push(symbolPick);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last separator with number of symbols we found for kind
|
||||||
|
updateLastSeparatorLabel();
|
||||||
|
} else {
|
||||||
|
symbolPicks = [
|
||||||
|
{ label: localize('symbols', "symbols ({0})", filteredSymbolPicks.length), type: 'separator' },
|
||||||
|
...sortedFilteredSymbolPicks
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbolPicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private compareByScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
|
||||||
|
if (!symbolA.score && symbolB.score) {
|
||||||
|
return 1;
|
||||||
|
} else if (symbolA.score && !symbolB.score) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbolA.score && symbolB.score) {
|
||||||
|
if (symbolA.score[0] > symbolB.score[0]) {
|
||||||
|
return -1;
|
||||||
|
} else if (symbolA.score[0] < symbolB.score[0]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbolA.index < symbolB.index) {
|
||||||
|
return -1;
|
||||||
|
} else if (symbolA.index > symbolB.index) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private compareByKindAndScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
|
||||||
|
const kindA = NLS_SYMBOL_KIND_CACHE[symbolA.kind] || FALLBACK_NLS_SYMBOL_KIND;
|
||||||
|
const kindB = NLS_SYMBOL_KIND_CACHE[symbolB.kind] || FALLBACK_NLS_SYMBOL_KIND;
|
||||||
|
|
||||||
|
// Sort by type first if scoped search
|
||||||
|
const result = kindA.localeCompare(kindB);
|
||||||
|
if (result === 0) {
|
||||||
|
return this.compareByScore(symbolA, symbolB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
|
||||||
|
const model = await OutlineModel.create(document, token);
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const roots: DocumentSymbol[] = [];
|
||||||
|
for (const child of values(model.children)) {
|
||||||
|
if (child instanceof OutlineElement) {
|
||||||
|
roots.push(child.symbol);
|
||||||
|
} else {
|
||||||
|
roots.push(...values(child.children).map(child => child.symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let flatEntries: DocumentSymbol[] = [];
|
||||||
|
if (flatten) {
|
||||||
|
this.flattenDocumentSymbols(flatEntries, roots, '');
|
||||||
|
} else {
|
||||||
|
flatEntries = roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flatEntries.sort((symbolA, symbolB) => Range.compareRangesUsingStarts(symbolA.range, symbolB.range));
|
||||||
|
}
|
||||||
|
|
||||||
|
private flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
|
||||||
|
for (const entry of entries) {
|
||||||
|
bucket.push({
|
||||||
|
kind: entry.kind,
|
||||||
|
tags: entry.tags,
|
||||||
|
name: entry.name,
|
||||||
|
detail: entry.detail,
|
||||||
|
containerName: entry.containerName || overrideContainerLabel,
|
||||||
|
range: entry.range,
|
||||||
|
selectionRange: entry.selectionRange,
|
||||||
|
children: undefined, // we flatten it...
|
||||||
|
});
|
||||||
|
|
||||||
|
// Recurse over children
|
||||||
|
if (entry.children) {
|
||||||
|
this.flattenDocumentSymbols(bucket, entry.children, entry.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region NLS Helpers
|
||||||
|
|
||||||
|
const FALLBACK_NLS_SYMBOL_KIND = localize('property', "properties ({0})");
|
||||||
|
const NLS_SYMBOL_KIND_CACHE: { [type: number]: string } = {
|
||||||
|
[SymbolKind.Method]: localize('method', "methods ({0})"),
|
||||||
|
[SymbolKind.Function]: localize('function', "functions ({0})"),
|
||||||
|
[SymbolKind.Constructor]: localize('_constructor', "constructors ({0})"),
|
||||||
|
[SymbolKind.Variable]: localize('variable', "variables ({0})"),
|
||||||
|
[SymbolKind.Class]: localize('class', "classes ({0})"),
|
||||||
|
[SymbolKind.Struct]: localize('struct', "structs ({0})"),
|
||||||
|
[SymbolKind.Event]: localize('event', "events ({0})"),
|
||||||
|
[SymbolKind.Operator]: localize('operator', "operators ({0})"),
|
||||||
|
[SymbolKind.Interface]: localize('interface', "interfaces ({0})"),
|
||||||
|
[SymbolKind.Namespace]: localize('namespace', "namespaces ({0})"),
|
||||||
|
[SymbolKind.Package]: localize('package', "packages ({0})"),
|
||||||
|
[SymbolKind.TypeParameter]: localize('typeParameter', "type parameters ({0})"),
|
||||||
|
[SymbolKind.Module]: localize('modules', "modules ({0})"),
|
||||||
|
[SymbolKind.Property]: localize('property', "properties ({0})"),
|
||||||
|
[SymbolKind.Enum]: localize('enum', "enumerations ({0})"),
|
||||||
|
[SymbolKind.EnumMember]: localize('enumMember', "enumeration members ({0})"),
|
||||||
|
[SymbolKind.String]: localize('string', "strings ({0})"),
|
||||||
|
[SymbolKind.File]: localize('file', "files ({0})"),
|
||||||
|
[SymbolKind.Array]: localize('array', "arrays ({0})"),
|
||||||
|
[SymbolKind.Number]: localize('number', "numbers ({0})"),
|
||||||
|
[SymbolKind.Boolean]: localize('boolean', "booleans ({0})"),
|
||||||
|
[SymbolKind.Object]: localize('object', "objects ({0})"),
|
||||||
|
[SymbolKind.Key]: localize('key', "keys ({0})"),
|
||||||
|
[SymbolKind.Field]: localize('field', "fields ({0})"),
|
||||||
|
[SymbolKind.Constant]: localize('constant', "constants ({0})")
|
||||||
|
};
|
||||||
|
|
||||||
|
//#endregion
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
|
||||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
|
||||||
import { IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
|
|
||||||
import { IRange } from 'vs/editor/common/core/range';
|
|
||||||
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
|
|
||||||
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
|
|
||||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { Event } from 'vs/base/common/event';
|
|
||||||
|
|
||||||
interface IEditorLineDecoration {
|
|
||||||
rangeHighlightId: string;
|
|
||||||
overviewRulerDecorationId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reusable quick access provider for the editor with support for adding decorations.
|
|
||||||
*/
|
|
||||||
export abstract class AbstractEditorQuickAccessProvider implements IQuickAccessProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses to provide an event when the active editor control changes.
|
|
||||||
*/
|
|
||||||
abstract readonly onDidActiveTextEditorControlChange: Event<void>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses to provide the current active editor control.
|
|
||||||
*/
|
|
||||||
abstract activeTextEditorControl: IEditor | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses to implement the quick access picker.
|
|
||||||
*/
|
|
||||||
abstract provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
|
|
||||||
|
|
||||||
|
|
||||||
//#region Decorations Utils
|
|
||||||
|
|
||||||
private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
|
|
||||||
|
|
||||||
protected addDecorations(editor: IEditor, range: IRange): void {
|
|
||||||
editor.changeDecorations(changeAccessor => {
|
|
||||||
|
|
||||||
// Reset old decorations if any
|
|
||||||
const deleteDecorations: string[] = [];
|
|
||||||
if (this.rangeHighlightDecorationId) {
|
|
||||||
deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
|
|
||||||
deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
|
|
||||||
|
|
||||||
this.rangeHighlightDecorationId = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new decorations for the range
|
|
||||||
const newDecorations: IModelDeltaDecoration[] = [
|
|
||||||
|
|
||||||
// highlight the entire line on the range
|
|
||||||
{
|
|
||||||
range,
|
|
||||||
options: {
|
|
||||||
className: 'rangeHighlight',
|
|
||||||
isWholeLine: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// also add overview ruler highlight
|
|
||||||
{
|
|
||||||
range,
|
|
||||||
options: {
|
|
||||||
overviewRuler: {
|
|
||||||
color: themeColorFromId(overviewRulerRangeHighlight),
|
|
||||||
position: OverviewRulerLane.Full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
|
|
||||||
|
|
||||||
this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected clearDecorations(editor: IEditor): void {
|
|
||||||
const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
|
|
||||||
if (rangeHighlightDecorationId) {
|
|
||||||
editor.changeDecorations(changeAccessor => {
|
|
||||||
changeAccessor.deltaDecorations([
|
|
||||||
rangeHighlightDecorationId.overviewRulerDecorationId,
|
|
||||||
rangeHighlightDecorationId.rangeHighlightId
|
|
||||||
], []);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.rangeHighlightDecorationId = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
}
|
|
||||||
@@ -7,11 +7,9 @@ import 'vs/editor/editor.all';
|
|||||||
import 'vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp';
|
import 'vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp';
|
||||||
import 'vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard';
|
import 'vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard';
|
||||||
import 'vs/editor/standalone/browser/inspectTokens/inspectTokens';
|
import 'vs/editor/standalone/browser/inspectTokens/inspectTokens';
|
||||||
import 'vs/editor/standalone/browser/quickOpen/gotoLine';
|
|
||||||
import 'vs/editor/standalone/browser/quickOpen/quickCommand';
|
|
||||||
import 'vs/editor/standalone/browser/quickOpen/quickOutline';
|
|
||||||
import 'vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess';
|
import 'vs/editor/standalone/browser/quickAccess/standaloneHelpQuickAccess';
|
||||||
import 'vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess';
|
import 'vs/editor/standalone/browser/quickAccess/standaloneGotoLineQuickAccess';
|
||||||
|
import 'vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess';
|
||||||
import 'vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess';
|
import 'vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess';
|
||||||
import 'vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch';
|
import 'vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch';
|
||||||
import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast';
|
import 'vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast';
|
||||||
|
|||||||
@@ -11,15 +11,20 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
|
|||||||
import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/commandsQuickAccess';
|
import { AbstractEditorCommandsQuickAccessProvider } from 'vs/editor/contrib/quickAccess/commandsQuickAccess';
|
||||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||||
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
|
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||||
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
|
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
|
||||||
export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider {
|
export class StandaloneCommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider {
|
||||||
|
|
||||||
get activeTextEditorControl(): IEditor | undefined { return withNullAsUndefined(this.codeEditorService.getFocusedCodeEditor()); }
|
protected get activeTextEditorControl(): IEditor | undefined { return withNullAsUndefined(this.codeEditorService.getFocusedCodeEditor()); }
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IInstantiationService instantiationService: IInstantiationService,
|
@IInstantiationService instantiationService: IInstantiationService,
|
||||||
@@ -42,3 +47,30 @@ Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessPro
|
|||||||
prefix: StandaloneCommandsQuickAccessProvider.PREFIX,
|
prefix: StandaloneCommandsQuickAccessProvider.PREFIX,
|
||||||
helpEntries: [{ description: QuickCommandNLS.quickCommandHelp, needsEditor: true }]
|
helpEntries: [{ description: QuickCommandNLS.quickCommandHelp, needsEditor: true }]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export class GotoLineAction extends EditorAction {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
id: 'editor.action.quickCommand',
|
||||||
|
label: QuickCommandNLS.quickCommandActionLabel,
|
||||||
|
alias: 'Command Palette',
|
||||||
|
precondition: undefined,
|
||||||
|
kbOpts: {
|
||||||
|
kbExpr: EditorContextKeys.focus,
|
||||||
|
primary: KeyCode.F1,
|
||||||
|
weight: KeybindingWeight.EditorContrib
|
||||||
|
},
|
||||||
|
contextMenuOpts: {
|
||||||
|
group: 'z_commands',
|
||||||
|
order: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run(accessor: ServicesAccessor): void {
|
||||||
|
accessor.get(IQuickInputService).quickAccess.show(StandaloneCommandsQuickAccessProvider.PREFIX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEditorAction(GotoLineAction);
|
||||||
|
|||||||
@@ -10,22 +10,51 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
|
|||||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
import { GoToLineNLS } from 'vs/editor/common/standaloneStrings';
|
import { GoToLineNLS } from 'vs/editor/common/standaloneStrings';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
|
import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||||
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
|
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||||
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
|
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
|
||||||
export class StandaloneGotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider {
|
export class StandaloneGotoLineQuickAccessProvider extends AbstractGotoLineQuickAccessProvider {
|
||||||
|
|
||||||
readonly onDidActiveTextEditorControlChange = Event.None;
|
protected readonly onDidActiveTextEditorControlChange = Event.None;
|
||||||
|
|
||||||
constructor(@ICodeEditorService private readonly editorService: ICodeEditorService) {
|
constructor(@ICodeEditorService private readonly editorService: ICodeEditorService) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeTextEditorControl() {
|
protected get activeTextEditorControl() {
|
||||||
return withNullAsUndefined(this.editorService.getFocusedCodeEditor());
|
return withNullAsUndefined(this.editorService.getFocusedCodeEditor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
|
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
|
||||||
ctor: StandaloneGotoLineQuickAccessProvider,
|
ctor: StandaloneGotoLineQuickAccessProvider,
|
||||||
prefix: AbstractGotoLineQuickAccessProvider.PREFIX,
|
prefix: StandaloneGotoLineQuickAccessProvider.PREFIX,
|
||||||
helpEntries: [{ description: GoToLineNLS.gotoLineActionLabel, needsEditor: true }]
|
helpEntries: [{ description: GoToLineNLS.gotoLineActionLabel, needsEditor: true }]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export class GotoLineAction extends EditorAction {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
id: 'editor.action.gotoLine',
|
||||||
|
label: GoToLineNLS.gotoLineActionLabel,
|
||||||
|
alias: 'Go to Line...',
|
||||||
|
precondition: undefined,
|
||||||
|
kbOpts: {
|
||||||
|
kbExpr: EditorContextKeys.focus,
|
||||||
|
primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
|
||||||
|
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G },
|
||||||
|
weight: KeybindingWeight.EditorContrib
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run(accessor: ServicesAccessor): void {
|
||||||
|
accessor.get(IQuickInputService).quickAccess.show(StandaloneGotoLineQuickAccessProvider.PREFIX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEditorAction(GotoLineAction);
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { AbstractGotoSymbolQuickAccessProvider } from 'vs/editor/contrib/quickAccess/gotoSymbolQuickAccess';
|
||||||
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
|
import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess';
|
||||||
|
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||||
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
|
import { QuickOutlineNLS } from 'vs/editor/common/standaloneStrings';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
|
import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||||
|
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||||
|
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||||
|
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||||
|
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
|
||||||
|
export class StandaloneGotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
|
||||||
|
|
||||||
|
protected readonly onDidActiveTextEditorControlChange = Event.None;
|
||||||
|
|
||||||
|
constructor(@ICodeEditorService private readonly editorService: ICodeEditorService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get activeTextEditorControl() {
|
||||||
|
return withNullAsUndefined(this.editorService.getFocusedCodeEditor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
|
||||||
|
ctor: StandaloneGotoSymbolQuickAccessProvider,
|
||||||
|
prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX,
|
||||||
|
helpEntries: [
|
||||||
|
{ description: QuickOutlineNLS.quickOutlineActionLabel, prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX, needsEditor: true },
|
||||||
|
{ description: QuickOutlineNLS.quickOutlineByCategoryActionLabel, prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY, needsEditor: true }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export class GotoLineAction extends EditorAction {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
id: 'editor.action.quickOutline',
|
||||||
|
label: QuickOutlineNLS.quickOutlineActionLabel,
|
||||||
|
alias: 'Go to Symbol...',
|
||||||
|
precondition: EditorContextKeys.hasDocumentSymbolProvider,
|
||||||
|
kbOpts: {
|
||||||
|
kbExpr: EditorContextKeys.focus,
|
||||||
|
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O,
|
||||||
|
weight: KeybindingWeight.EditorContrib
|
||||||
|
},
|
||||||
|
contextMenuOpts: {
|
||||||
|
group: 'navigation',
|
||||||
|
order: 3
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
run(accessor: ServicesAccessor): void {
|
||||||
|
accessor.get(IQuickInputService).quickAccess.show(AbstractGotoSymbolQuickAccessProvider.PREFIX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEditorAction(GotoLineAction);
|
||||||
@@ -8,8 +8,8 @@ import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/
|
|||||||
import { QuickHelpNLS } from 'vs/editor/common/standaloneStrings';
|
import { QuickHelpNLS } from 'vs/editor/common/standaloneStrings';
|
||||||
import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess';
|
import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess';
|
||||||
|
|
||||||
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).defaultProvider = {
|
Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess).registerQuickAccessProvider({
|
||||||
ctor: HelpQuickAccessProvider,
|
ctor: HelpQuickAccessProvider,
|
||||||
prefix: '',
|
prefix: '',
|
||||||
helpEntries: [{ description: QuickHelpNLS.helpQuickAccessActionLabel, needsEditor: true }]
|
helpEntries: [{ description: QuickHelpNLS.helpQuickAccessActionLabel, needsEditor: true }]
|
||||||
};
|
});
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
|
|
||||||
.monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
|
||||||
color: #0066BF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vs-dark .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
|
|
||||||
.vs-dark .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
|
||||||
color: #0097fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hc-black .monaco-quick-open-widget .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
|
|
||||||
.hc-black .monaco-quick-open-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
|
||||||
color: #F38518;
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!./editorQuickOpen';
|
|
||||||
import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
||||||
import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { EditorAction, IActionOptions, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
|
||||||
import { Selection } from 'vs/editor/common/core/selection';
|
|
||||||
import { IEditorContribution, ScrollType, IEditor } from 'vs/editor/common/editorCommon';
|
|
||||||
import { IModelDeltaDecoration } from 'vs/editor/common/model';
|
|
||||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
|
||||||
import { QuickOpenEditorWidget } from 'vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
|
|
||||||
export interface IQuickOpenControllerOpts {
|
|
||||||
inputAriaLabel: string;
|
|
||||||
getModel(value: string): QuickOpenModel;
|
|
||||||
getAutoFocus(searchValue: string): IAutoFocus;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QuickOpenController implements IEditorContribution, IDecorator {
|
|
||||||
|
|
||||||
public static readonly ID = 'editor.controller.quickOpenController';
|
|
||||||
|
|
||||||
public static get(editor: ICodeEditor): QuickOpenController {
|
|
||||||
return editor.getContribution<QuickOpenController>(QuickOpenController.ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly editor: ICodeEditor;
|
|
||||||
private widget: QuickOpenEditorWidget | null = null;
|
|
||||||
private rangeHighlightDecorationId: string | null = null;
|
|
||||||
private lastKnownEditorSelection: Selection | null = null;
|
|
||||||
|
|
||||||
constructor(editor: ICodeEditor, @IThemeService private readonly themeService: IThemeService) {
|
|
||||||
this.editor = editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispose(): void {
|
|
||||||
// Dispose widget
|
|
||||||
if (this.widget) {
|
|
||||||
this.widget.destroy();
|
|
||||||
this.widget = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(opts: IQuickOpenControllerOpts): void {
|
|
||||||
if (this.widget) {
|
|
||||||
this.widget.destroy();
|
|
||||||
this.widget = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create goto line widget
|
|
||||||
let onClose = (canceled: boolean) => {
|
|
||||||
// Clear Highlight Decorations if present
|
|
||||||
this.clearDecorations();
|
|
||||||
|
|
||||||
// Restore selection if canceled
|
|
||||||
if (canceled && this.lastKnownEditorSelection) {
|
|
||||||
this.editor.setSelection(this.lastKnownEditorSelection);
|
|
||||||
this.editor.revealRangeInCenterIfOutsideViewport(this.lastKnownEditorSelection, ScrollType.Smooth);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastKnownEditorSelection = null;
|
|
||||||
|
|
||||||
// Return focus to the editor if
|
|
||||||
// - focus is back on the <body> element because no other focusable element was clicked
|
|
||||||
// - a command was picked from the picker which indicates the editor should get focused
|
|
||||||
if (document.activeElement === document.body || !canceled) {
|
|
||||||
this.editor.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.widget = new QuickOpenEditorWidget(
|
|
||||||
this.editor,
|
|
||||||
() => onClose(false),
|
|
||||||
() => onClose(true),
|
|
||||||
(value: string) => {
|
|
||||||
this.widget!.setInput(opts.getModel(value), opts.getAutoFocus(value));
|
|
||||||
},
|
|
||||||
{
|
|
||||||
inputAriaLabel: opts.inputAriaLabel
|
|
||||||
},
|
|
||||||
this.themeService
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remember selection to be able to restore on cancel
|
|
||||||
if (!this.lastKnownEditorSelection) {
|
|
||||||
this.lastKnownEditorSelection = this.editor.getSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show
|
|
||||||
this.widget.show('');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly _RANGE_HIGHLIGHT_DECORATION = ModelDecorationOptions.register({
|
|
||||||
className: 'rangeHighlight',
|
|
||||||
isWholeLine: true
|
|
||||||
});
|
|
||||||
|
|
||||||
public decorateLine(range: Range, editor: ICodeEditor): void {
|
|
||||||
const oldDecorations: string[] = [];
|
|
||||||
if (this.rangeHighlightDecorationId) {
|
|
||||||
oldDecorations.push(this.rangeHighlightDecorationId);
|
|
||||||
this.rangeHighlightDecorationId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newDecorations: IModelDeltaDecoration[] = [
|
|
||||||
{
|
|
||||||
range: range,
|
|
||||||
options: QuickOpenController._RANGE_HIGHLIGHT_DECORATION
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const decorations = editor.deltaDecorations(oldDecorations, newDecorations);
|
|
||||||
this.rangeHighlightDecorationId = decorations[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public clearDecorations(): void {
|
|
||||||
if (this.rangeHighlightDecorationId) {
|
|
||||||
this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
|
|
||||||
this.rangeHighlightDecorationId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IQuickOpenOpts {
|
|
||||||
/**
|
|
||||||
* provide the quick open model for the given search value.
|
|
||||||
*/
|
|
||||||
getModel(value: string): QuickOpenModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* provide the quick open auto focus mode for the given search value.
|
|
||||||
*/
|
|
||||||
getAutoFocus(searchValue: string): IAutoFocus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for providing quick open in the editor.
|
|
||||||
*/
|
|
||||||
export abstract class BaseEditorQuickOpenAction extends EditorAction {
|
|
||||||
|
|
||||||
private readonly _inputAriaLabel: string;
|
|
||||||
|
|
||||||
constructor(inputAriaLabel: string, opts: IActionOptions) {
|
|
||||||
super(opts);
|
|
||||||
this._inputAriaLabel = inputAriaLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getController(editor: ICodeEditor): QuickOpenController {
|
|
||||||
return QuickOpenController.get(editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _show(controller: QuickOpenController, opts: IQuickOpenOpts): void {
|
|
||||||
controller.run({
|
|
||||||
inputAriaLabel: this._inputAriaLabel,
|
|
||||||
getModel: (value: string): QuickOpenModel => opts.getModel(value),
|
|
||||||
getAutoFocus: (searchValue: string): IAutoFocus => opts.getAutoFocus(searchValue)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDecorator {
|
|
||||||
decorateLine(range: Range, editor: IEditor): void;
|
|
||||||
clearDecorations(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerEditorContribution(QuickOpenController.ID, QuickOpenController);
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.monaco-quick-open-widget {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!./gotoLine';
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
|
||||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
|
||||||
import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
||||||
import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
||||||
import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
|
||||||
import { Position } from 'vs/editor/common/core/position';
|
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
|
||||||
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
|
|
||||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|
||||||
import { ITextModel } from 'vs/editor/common/model';
|
|
||||||
import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
|
|
||||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
|
||||||
import { GoToLineNLS } from 'vs/editor/common/standaloneStrings';
|
|
||||||
|
|
||||||
interface ParseResult {
|
|
||||||
position: Position;
|
|
||||||
isValid: boolean;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GotoLineEntry extends QuickOpenEntry {
|
|
||||||
private readonly parseResult: ParseResult;
|
|
||||||
private readonly decorator: IDecorator;
|
|
||||||
private readonly editor: IEditor;
|
|
||||||
|
|
||||||
constructor(line: string, editor: IEditor, decorator: IDecorator) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.editor = editor;
|
|
||||||
this.decorator = decorator;
|
|
||||||
this.parseResult = this.parseInput(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseInput(line: string): ParseResult {
|
|
||||||
const numbers = line.split(',').map(part => parseInt(part, 10)).filter(part => !isNaN(part));
|
|
||||||
let position: Position;
|
|
||||||
|
|
||||||
if (numbers.length === 0) {
|
|
||||||
position = new Position(-1, -1);
|
|
||||||
} else if (numbers.length === 1) {
|
|
||||||
position = new Position(numbers[0], 1);
|
|
||||||
} else {
|
|
||||||
position = new Position(numbers[0], numbers[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let model: ITextModel | null;
|
|
||||||
if (isCodeEditor(this.editor)) {
|
|
||||||
model = this.editor.getModel();
|
|
||||||
} else {
|
|
||||||
const diffModel = (<IDiffEditor>this.editor).getModel();
|
|
||||||
model = diffModel ? diffModel.modified : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValid = model ? model.validatePosition(position).equals(position) : false;
|
|
||||||
let label: string;
|
|
||||||
|
|
||||||
if (isValid) {
|
|
||||||
if (position.column && position.column > 1) {
|
|
||||||
label = strings.format(GoToLineNLS.gotoLineLabelValidLineAndColumn, position.lineNumber, position.column);
|
|
||||||
} else {
|
|
||||||
label = strings.format(GoToLineNLS.gotoLineLabelValidLine, position.lineNumber);
|
|
||||||
}
|
|
||||||
} else if (position.lineNumber < 1 || position.lineNumber > (model ? model.getLineCount() : 0)) {
|
|
||||||
label = strings.format(GoToLineNLS.gotoLineLabelEmptyWithLineLimit, model ? model.getLineCount() : 0);
|
|
||||||
} else {
|
|
||||||
label = strings.format(GoToLineNLS.gotoLineLabelEmptyWithLineAndColumnLimit, model ? model.getLineMaxColumn(position.lineNumber) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
position: position,
|
|
||||||
isValid: isValid,
|
|
||||||
label: label
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getLabel(): string {
|
|
||||||
return this.parseResult.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAriaLabel(): string {
|
|
||||||
const position = this.editor.getPosition();
|
|
||||||
const currentLine = position ? position.lineNumber : 0;
|
|
||||||
return strings.format(GoToLineNLS.gotoLineAriaLabel, currentLine, this.parseResult.label);
|
|
||||||
}
|
|
||||||
|
|
||||||
run(mode: Mode, _context: IEntryRunContext): boolean {
|
|
||||||
if (mode === Mode.OPEN) {
|
|
||||||
return this.runOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.runPreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
runOpen(): boolean {
|
|
||||||
|
|
||||||
// No-op if range is not valid
|
|
||||||
if (!this.parseResult.isValid) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply selection and focus
|
|
||||||
const range = this.toSelection();
|
|
||||||
(<ICodeEditor>this.editor).setSelection(range);
|
|
||||||
(<ICodeEditor>this.editor).revealRangeInCenter(range, ScrollType.Smooth);
|
|
||||||
this.editor.focus();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
runPreview(): boolean {
|
|
||||||
|
|
||||||
// No-op if range is not valid
|
|
||||||
if (!this.parseResult.isValid) {
|
|
||||||
this.decorator.clearDecorations();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select Line Position
|
|
||||||
const range = this.toSelection();
|
|
||||||
this.editor.revealRangeInCenter(range, ScrollType.Smooth);
|
|
||||||
|
|
||||||
// Decorate if possible
|
|
||||||
this.decorator.decorateLine(range, this.editor);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toSelection(): Range {
|
|
||||||
return new Range(
|
|
||||||
this.parseResult.position.lineNumber,
|
|
||||||
this.parseResult.position.column,
|
|
||||||
this.parseResult.position.lineNumber,
|
|
||||||
this.parseResult.position.column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GotoLineAction extends BaseEditorQuickOpenAction {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(GoToLineNLS.gotoLineActionInput, {
|
|
||||||
id: 'editor.action.gotoLine',
|
|
||||||
label: GoToLineNLS.gotoLineActionLabel,
|
|
||||||
alias: 'Go to Line...',
|
|
||||||
precondition: undefined,
|
|
||||||
kbOpts: {
|
|
||||||
kbExpr: EditorContextKeys.focus,
|
|
||||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_G,
|
|
||||||
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G },
|
|
||||||
weight: KeybindingWeight.EditorContrib
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
||||||
this._show(this.getController(editor), {
|
|
||||||
getModel: (value: string): QuickOpenModel => {
|
|
||||||
return new QuickOpenModel([new GotoLineEntry(value, editor, this.getController(editor))]);
|
|
||||||
},
|
|
||||||
|
|
||||||
getAutoFocus: (searchValue: string): IAutoFocus => {
|
|
||||||
return {
|
|
||||||
autoFocusFirstEntry: searchValue.length > 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerEditorAction(GotoLineAction);
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
|
||||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
|
||||||
import { matchesFuzzy } from 'vs/base/common/filters';
|
|
||||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
|
||||||
import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
||||||
import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
|
||||||
import { IEditor, IEditorAction } from 'vs/editor/common/editorCommon';
|
|
||||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|
||||||
import { BaseEditorQuickOpenAction } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
|
|
||||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
|
||||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
|
||||||
import { QuickCommandNLS } from 'vs/editor/common/standaloneStrings';
|
|
||||||
|
|
||||||
export class EditorActionCommandEntry extends QuickOpenEntryGroup {
|
|
||||||
private readonly key: string;
|
|
||||||
private readonly action: IEditorAction;
|
|
||||||
private readonly editor: IEditor;
|
|
||||||
private readonly keyAriaLabel: string;
|
|
||||||
|
|
||||||
constructor(key: string, keyAriaLabel: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
this.keyAriaLabel = keyAriaLabel;
|
|
||||||
this.setHighlights(highlights);
|
|
||||||
this.action = action;
|
|
||||||
this.editor = editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLabel(): string {
|
|
||||||
return this.action.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAriaLabel(): string {
|
|
||||||
if (this.keyAriaLabel) {
|
|
||||||
return strings.format(QuickCommandNLS.ariaLabelEntryWithKey, this.getLabel(), this.keyAriaLabel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.format(QuickCommandNLS.ariaLabelEntry, this.getLabel());
|
|
||||||
}
|
|
||||||
|
|
||||||
public getGroupLabel(): string {
|
|
||||||
return this.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(mode: Mode, context: IEntryRunContext): boolean {
|
|
||||||
if (mode === Mode.OPEN) {
|
|
||||||
|
|
||||||
// Use a timeout to give the quick open widget a chance to close itself first
|
|
||||||
setTimeout(() => {
|
|
||||||
|
|
||||||
// Some actions are enabled only when editor has focus
|
|
||||||
this.editor.focus();
|
|
||||||
|
|
||||||
try {
|
|
||||||
let promise = this.action.run() || Promise.resolve();
|
|
||||||
promise.then(undefined, onUnexpectedError);
|
|
||||||
} catch (error) {
|
|
||||||
onUnexpectedError(error);
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QuickCommandAction extends BaseEditorQuickOpenAction {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(QuickCommandNLS.quickCommandActionInput, {
|
|
||||||
id: 'editor.action.quickCommand',
|
|
||||||
label: QuickCommandNLS.quickCommandActionLabel,
|
|
||||||
alias: 'Command Palette',
|
|
||||||
precondition: undefined,
|
|
||||||
kbOpts: {
|
|
||||||
kbExpr: EditorContextKeys.focus,
|
|
||||||
primary: KeyCode.F1,
|
|
||||||
weight: KeybindingWeight.EditorContrib
|
|
||||||
},
|
|
||||||
contextMenuOpts: {
|
|
||||||
group: 'z_commands',
|
|
||||||
order: 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
|
||||||
const keybindingService = accessor.get(IKeybindingService);
|
|
||||||
|
|
||||||
this._show(this.getController(editor), {
|
|
||||||
getModel: (value: string): QuickOpenModel => {
|
|
||||||
return new QuickOpenModel(this._editorActionsToEntries(keybindingService, editor, value));
|
|
||||||
},
|
|
||||||
|
|
||||||
getAutoFocus: (searchValue: string): IAutoFocus => {
|
|
||||||
return {
|
|
||||||
autoFocusFirstEntry: true,
|
|
||||||
autoFocusPrefixMatch: searchValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _sort(elementA: QuickOpenEntryGroup, elementB: QuickOpenEntryGroup): number {
|
|
||||||
let elementAName = (elementA.getLabel() || '').toLowerCase();
|
|
||||||
let elementBName = (elementB.getLabel() || '').toLowerCase();
|
|
||||||
|
|
||||||
return elementAName.localeCompare(elementBName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _editorActionsToEntries(keybindingService: IKeybindingService, editor: ICodeEditor, searchValue: string): EditorActionCommandEntry[] {
|
|
||||||
let actions: IEditorAction[] = editor.getSupportedActions();
|
|
||||||
let entries: EditorActionCommandEntry[] = [];
|
|
||||||
|
|
||||||
for (const action of actions) {
|
|
||||||
|
|
||||||
let keybinding = keybindingService.lookupKeybinding(action.id);
|
|
||||||
|
|
||||||
if (action.label) {
|
|
||||||
let highlights = matchesFuzzy(searchValue, action.label);
|
|
||||||
if (highlights) {
|
|
||||||
entries.push(new EditorActionCommandEntry(keybinding ? keybinding.getLabel() || '' : '', keybinding ? keybinding.getAriaLabel() || '' : '', highlights, action, editor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by name
|
|
||||||
entries = entries.sort(this._sort);
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerEditorAction(QuickCommandAction);
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { Dimension } from 'vs/base/browser/dom';
|
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
||||||
import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
|
|
||||||
import { IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
||||||
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { foreground } from 'vs/platform/theme/common/colorRegistry';
|
|
||||||
import { attachQuickOpenStyler } from 'vs/platform/theme/common/styler';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|
||||||
|
|
||||||
export interface IQuickOpenEditorWidgetOptions {
|
|
||||||
inputAriaLabel: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QuickOpenEditorWidget implements IOverlayWidget {
|
|
||||||
|
|
||||||
private static readonly ID = 'editor.contrib.quickOpenEditorWidget';
|
|
||||||
|
|
||||||
private readonly codeEditor: ICodeEditor;
|
|
||||||
private readonly themeService: IThemeService;
|
|
||||||
private visible: boolean | undefined;
|
|
||||||
private quickOpenWidget: QuickOpenWidget;
|
|
||||||
private domNode: HTMLElement;
|
|
||||||
private styler: IDisposable;
|
|
||||||
|
|
||||||
constructor(codeEditor: ICodeEditor, onOk: () => void, onCancel: () => void, onType: (value: string) => void, configuration: IQuickOpenEditorWidgetOptions, themeService: IThemeService) {
|
|
||||||
this.codeEditor = codeEditor;
|
|
||||||
this.themeService = themeService;
|
|
||||||
this.visible = false;
|
|
||||||
|
|
||||||
this.domNode = document.createElement('div');
|
|
||||||
|
|
||||||
this.quickOpenWidget = new QuickOpenWidget(
|
|
||||||
this.domNode,
|
|
||||||
{
|
|
||||||
onOk: onOk,
|
|
||||||
onCancel: onCancel,
|
|
||||||
onType: onType
|
|
||||||
}, {
|
|
||||||
inputPlaceHolder: undefined,
|
|
||||||
inputAriaLabel: configuration.inputAriaLabel,
|
|
||||||
keyboardSupport: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.styler = attachQuickOpenStyler(this.quickOpenWidget, this.themeService, {
|
|
||||||
pickerGroupForeground: foreground
|
|
||||||
});
|
|
||||||
|
|
||||||
this.quickOpenWidget.create();
|
|
||||||
this.codeEditor.addOverlayWidget(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setInput(model: QuickOpenModel, focus: IAutoFocus): void {
|
|
||||||
this.quickOpenWidget.setInput(model, focus);
|
|
||||||
}
|
|
||||||
|
|
||||||
getId(): string {
|
|
||||||
return QuickOpenEditorWidget.ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDomNode(): HTMLElement {
|
|
||||||
return this.domNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
this.codeEditor.removeOverlayWidget(this);
|
|
||||||
this.quickOpenWidget.dispose();
|
|
||||||
this.styler.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
isVisible(): boolean {
|
|
||||||
return !!this.visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
show(value: string): void {
|
|
||||||
this.visible = true;
|
|
||||||
|
|
||||||
const editorLayout = this.codeEditor.getLayoutInfo();
|
|
||||||
if (editorLayout) {
|
|
||||||
this.quickOpenWidget.layout(new Dimension(editorLayout.width, editorLayout.height));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.quickOpenWidget.show(value);
|
|
||||||
this.codeEditor.layoutOverlayWidget(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
hide(): void {
|
|
||||||
this.visible = false;
|
|
||||||
this.quickOpenWidget.hide();
|
|
||||||
this.codeEditor.layoutOverlayWidget(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPosition(): IOverlayWidgetPosition | null {
|
|
||||||
if (this.visible) {
|
|
||||||
return {
|
|
||||||
preference: OverlayWidgetPositionPreference.TOP_CENTER
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
.monaco-quick-open-widget {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import 'vs/css!./quickOutline';
|
|
||||||
import 'vs/base/browser/ui/codiconLabel/codiconLabel'; // The codicon symbol styles are defined here and must be loaded
|
|
||||||
import 'vs/editor/contrib/documentSymbols/outlineTree'; // The codicon symbol colors are defined here and must be loaded
|
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
|
||||||
import { matchesFuzzy } from 'vs/base/common/filters';
|
|
||||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
|
||||||
import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
||||||
import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
||||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
||||||
import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
|
||||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
|
||||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
|
||||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|
||||||
import { DocumentSymbol, DocumentSymbolProviderRegistry, SymbolKinds } from 'vs/editor/common/modes';
|
|
||||||
import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen';
|
|
||||||
import { BaseEditorQuickOpenAction, IDecorator } from 'vs/editor/standalone/browser/quickOpen/editorQuickOpen';
|
|
||||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
|
||||||
import { QuickOutlineNLS } from 'vs/editor/common/standaloneStrings';
|
|
||||||
|
|
||||||
let SCOPE_PREFIX = ':';
|
|
||||||
|
|
||||||
export class SymbolEntry extends QuickOpenEntryGroup {
|
|
||||||
private readonly name: string;
|
|
||||||
private readonly type: string;
|
|
||||||
private readonly description: string | undefined;
|
|
||||||
private readonly range: Range;
|
|
||||||
private readonly editor: ICodeEditor;
|
|
||||||
private readonly decorator: IDecorator;
|
|
||||||
|
|
||||||
constructor(name: string, type: string, description: string | undefined, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.type = type;
|
|
||||||
this.description = description;
|
|
||||||
this.range = range;
|
|
||||||
this.setHighlights(highlights);
|
|
||||||
this.editor = editor;
|
|
||||||
this.decorator = decorator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLabel(): string {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getAriaLabel(): string {
|
|
||||||
return strings.format(QuickOutlineNLS.entryAriaLabel, this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getIcon(): string {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getDescription(): string | undefined {
|
|
||||||
return this.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getType(): string {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getRange(): Range {
|
|
||||||
return this.range;
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(mode: Mode, context: IEntryRunContext): boolean {
|
|
||||||
if (mode === Mode.OPEN) {
|
|
||||||
return this.runOpen(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.runPreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
private runOpen(_context: IEntryRunContext): boolean {
|
|
||||||
|
|
||||||
// Apply selection and focus
|
|
||||||
let range = this.toSelection();
|
|
||||||
this.editor.setSelection(range);
|
|
||||||
this.editor.revealRangeInCenter(range, ScrollType.Smooth);
|
|
||||||
this.editor.focus();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private runPreview(): boolean {
|
|
||||||
|
|
||||||
// Select Outline Position
|
|
||||||
let range = this.toSelection();
|
|
||||||
this.editor.revealRangeInCenter(range, ScrollType.Smooth);
|
|
||||||
|
|
||||||
// Decorate if possible
|
|
||||||
this.decorator.decorateLine(this.range, this.editor);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toSelection(): Range {
|
|
||||||
return new Range(
|
|
||||||
this.range.startLineNumber,
|
|
||||||
this.range.startColumn || 1,
|
|
||||||
this.range.startLineNumber,
|
|
||||||
this.range.startColumn || 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QuickOutlineAction extends BaseEditorQuickOpenAction {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(QuickOutlineNLS.quickOutlineActionInput, {
|
|
||||||
id: 'editor.action.quickOutline',
|
|
||||||
label: QuickOutlineNLS.quickOutlineActionLabel,
|
|
||||||
alias: 'Go to Symbol...',
|
|
||||||
precondition: EditorContextKeys.hasDocumentSymbolProvider,
|
|
||||||
kbOpts: {
|
|
||||||
kbExpr: EditorContextKeys.focus,
|
|
||||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O,
|
|
||||||
weight: KeybindingWeight.EditorContrib
|
|
||||||
},
|
|
||||||
contextMenuOpts: {
|
|
||||||
group: 'navigation',
|
|
||||||
order: 3
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(accessor: ServicesAccessor, editor: ICodeEditor) {
|
|
||||||
if (!editor.hasModel()) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const model = editor.getModel();
|
|
||||||
|
|
||||||
if (!DocumentSymbolProviderRegistry.has(model)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve outline
|
|
||||||
return getDocumentSymbols(model, true, CancellationToken.None).then((result: DocumentSymbol[]) => {
|
|
||||||
if (result.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._run(editor, result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _run(editor: ICodeEditor, result: DocumentSymbol[]): void {
|
|
||||||
this._show(this.getController(editor), {
|
|
||||||
getModel: (value: string): QuickOpenModel => {
|
|
||||||
return new QuickOpenModel(this.toQuickOpenEntries(editor, result, value));
|
|
||||||
},
|
|
||||||
|
|
||||||
getAutoFocus: (searchValue: string): IAutoFocus => {
|
|
||||||
// Remove any type pattern (:) from search value as needed
|
|
||||||
if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
|
|
||||||
searchValue = searchValue.substr(SCOPE_PREFIX.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
autoFocusPrefixMatch: searchValue,
|
|
||||||
autoFocusFirstEntry: !!searchValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private symbolEntry(name: string, type: string, description: string | undefined, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry {
|
|
||||||
return new SymbolEntry(name, type, description, Range.lift(range), highlights, editor, decorator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private toQuickOpenEntries(editor: ICodeEditor, flattened: DocumentSymbol[], searchValue: string): SymbolEntry[] {
|
|
||||||
const controller = this.getController(editor);
|
|
||||||
|
|
||||||
let results: SymbolEntry[] = [];
|
|
||||||
|
|
||||||
// Convert to Entries
|
|
||||||
let normalizedSearchValue = searchValue;
|
|
||||||
if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
|
|
||||||
normalizedSearchValue = normalizedSearchValue.substr(SCOPE_PREFIX.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const element of flattened) {
|
|
||||||
let label = strings.trim(element.name);
|
|
||||||
|
|
||||||
// Check for meatch
|
|
||||||
let highlights = matchesFuzzy(normalizedSearchValue, label);
|
|
||||||
if (highlights) {
|
|
||||||
|
|
||||||
// Show parent scope as description
|
|
||||||
let description: string | undefined = undefined;
|
|
||||||
if (element.containerName) {
|
|
||||||
description = element.containerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add
|
|
||||||
results.push(this.symbolEntry(label, SymbolKinds.toCssClassName(element.kind), description, element.range, highlights, editor, controller));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort properly if actually searching
|
|
||||||
if (searchValue) {
|
|
||||||
if (searchValue.indexOf(SCOPE_PREFIX) === 0) {
|
|
||||||
results = results.sort(this.sortScoped.bind(this, searchValue.toLowerCase()));
|
|
||||||
} else {
|
|
||||||
results = results.sort(this.sortNormal.bind(this, searchValue.toLowerCase()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark all type groups
|
|
||||||
if (results.length > 0 && searchValue.indexOf(SCOPE_PREFIX) === 0) {
|
|
||||||
let currentType: string | null = null;
|
|
||||||
let currentResult: SymbolEntry | null = null;
|
|
||||||
let typeCounter = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
|
||||||
let result = results[i];
|
|
||||||
|
|
||||||
// Found new type
|
|
||||||
if (currentType !== result.getType()) {
|
|
||||||
|
|
||||||
// Update previous result with count
|
|
||||||
if (currentResult) {
|
|
||||||
currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter));
|
|
||||||
}
|
|
||||||
|
|
||||||
currentType = result.getType();
|
|
||||||
currentResult = result;
|
|
||||||
typeCounter = 1;
|
|
||||||
|
|
||||||
result.setShowBorder(i > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Existing type, keep counting
|
|
||||||
else {
|
|
||||||
typeCounter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update previous result with count
|
|
||||||
if (currentResult) {
|
|
||||||
currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark first entry as outline
|
|
||||||
else if (results.length > 0) {
|
|
||||||
results[0].setGroupLabel(strings.format(QuickOutlineNLS._symbols_, results.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private typeToLabel(type: string, count: number): string {
|
|
||||||
switch (type) {
|
|
||||||
case 'module': return strings.format(QuickOutlineNLS._modules_, count);
|
|
||||||
case 'class': return strings.format(QuickOutlineNLS._class_, count);
|
|
||||||
case 'interface': return strings.format(QuickOutlineNLS._interface_, count);
|
|
||||||
case 'method': return strings.format(QuickOutlineNLS._method_, count);
|
|
||||||
case 'function': return strings.format(QuickOutlineNLS._function_, count);
|
|
||||||
case 'property': return strings.format(QuickOutlineNLS._property_, count);
|
|
||||||
case 'variable': return strings.format(QuickOutlineNLS._variable_, count);
|
|
||||||
case 'var': return strings.format(QuickOutlineNLS._variable2_, count);
|
|
||||||
case 'constructor': return strings.format(QuickOutlineNLS._constructor_, count);
|
|
||||||
case 'call': return strings.format(QuickOutlineNLS._call_, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number {
|
|
||||||
let elementAName = elementA.getLabel().toLowerCase();
|
|
||||||
let elementBName = elementB.getLabel().toLowerCase();
|
|
||||||
|
|
||||||
// Compare by name
|
|
||||||
let r = elementAName.localeCompare(elementBName);
|
|
||||||
if (r !== 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If name identical sort by range instead
|
|
||||||
let elementARange = elementA.getRange();
|
|
||||||
let elementBRange = elementB.getRange();
|
|
||||||
return elementARange.startLineNumber - elementBRange.startLineNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
private sortScoped(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number {
|
|
||||||
|
|
||||||
// Remove scope char
|
|
||||||
searchValue = searchValue.substr(SCOPE_PREFIX.length);
|
|
||||||
|
|
||||||
// Sort by type first if scoped search
|
|
||||||
let elementAType = elementA.getType();
|
|
||||||
let elementBType = elementB.getType();
|
|
||||||
let r = elementAType.localeCompare(elementBType);
|
|
||||||
if (r !== 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special sort when searching in scoped mode
|
|
||||||
if (searchValue) {
|
|
||||||
let elementAName = elementA.getLabel().toLowerCase();
|
|
||||||
let elementBName = elementB.getLabel().toLowerCase();
|
|
||||||
|
|
||||||
// Compare by name
|
|
||||||
let r = elementAName.localeCompare(elementBName);
|
|
||||||
if (r !== 0) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to sort by range
|
|
||||||
let elementARange = elementA.getRange();
|
|
||||||
let elementBRange = elementB.getRange();
|
|
||||||
return elementARange.startLineNumber - elementBRange.startLineNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerEditorAction(QuickOutlineAction);
|
|
||||||
@@ -138,6 +138,8 @@ class StandaloneTheme implements IStandaloneTheme {
|
|||||||
public get tokenColorMap(): string[] {
|
public get tokenColorMap(): string[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly semanticHighlighting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
|
function isBuiltinTheme(themeName: string): themeName is BuiltinTheme {
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ suite('TokenizationSupport2Adapter', () => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
semanticHighlighting: false,
|
||||||
|
|
||||||
tokenColorMap: []
|
tokenColorMap: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -74,6 +74,7 @@ declare namespace monaco {
|
|||||||
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
|
||||||
* and encoding.
|
* and encoding.
|
||||||
*
|
*
|
||||||
|
* ```txt
|
||||||
* foo://example.com:8042/over/there?name=ferret#nose
|
* foo://example.com:8042/over/there?name=ferret#nose
|
||||||
* \_/ \______________/\_________/ \_________/ \__/
|
* \_/ \______________/\_________/ \_________/ \__/
|
||||||
* | | | | |
|
* | | | | |
|
||||||
@@ -81,6 +82,7 @@ declare namespace monaco {
|
|||||||
* | _____________________|__
|
* | _____________________|__
|
||||||
* / \ / \
|
* / \ / \
|
||||||
* urn:example:animal:ferret:nose
|
* urn:example:animal:ferret:nose
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
export class Uri implements UriComponents {
|
export class Uri implements UriComponents {
|
||||||
static isUri(thing: any): thing is Uri;
|
static isUri(thing: any): thing is Uri;
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ export interface ParsedArgs {
|
|||||||
user?: string;
|
user?: string;
|
||||||
command?: string;
|
command?: string;
|
||||||
// {{SQL CARBON EDIT}} End
|
// {{SQL CARBON EDIT}} End
|
||||||
|
'sync'?: 'on' | 'off';
|
||||||
|
|
||||||
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
|
// chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches
|
||||||
'no-proxy-server'?: boolean;
|
'no-proxy-server'?: boolean;
|
||||||
'proxy-server'?: string;
|
'proxy-server'?: string;
|
||||||
@@ -173,5 +175,5 @@ export interface IEnvironmentService extends IUserHomeProvider {
|
|||||||
driverHandle?: string;
|
driverHandle?: string;
|
||||||
driverVerbose: boolean;
|
driverVerbose: boolean;
|
||||||
|
|
||||||
galleryMachineIdResource?: URI;
|
serviceMachineIdResource?: URI;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
|
|||||||
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
|
'telemetry': { type: 'boolean', cat: 'o', description: localize('telemetry', "Shows all telemetry events which VS code collects.") },
|
||||||
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
|
'folder-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
|
||||||
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
|
'file-uri': { type: 'string[]', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
|
||||||
|
'sync': { type: 'string', cat: 'o', description: localize('turn sync', "Turn sync on or off"), args: ['on', 'off'] },
|
||||||
|
|
||||||
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
|
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
|
||||||
'builtin-extensions-dir': { type: 'string' },
|
'builtin-extensions-dir': { type: 'string' },
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ export class EnvironmentService implements IEnvironmentService {
|
|||||||
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
|
get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; }
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
get galleryMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
|
get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); }
|
||||||
|
|
||||||
get disableUpdates(): boolean { return !!this._args['disable-updates']; }
|
get disableUpdates(): boolean { return !!this._args['disable-updates']; }
|
||||||
get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; }
|
get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { IRequestService, asJson, asText } from 'vs/platform/request/common/requ
|
|||||||
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
|
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
|
||||||
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
|
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { generateUuid, isUUID } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import { values } from 'vs/base/common/map';
|
import { values } from 'vs/base/common/map';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}}
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}}
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
@@ -22,11 +22,10 @@ import { IExtensionManifest, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/pl
|
|||||||
import { IFileService } from 'vs/platform/files/common/files';
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { joinPath } from 'vs/base/common/resources';
|
import { joinPath } from 'vs/base/common/resources';
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
|
||||||
import { IProductService } from 'vs/platform/product/common/productService';
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
|
||||||
import { find } from 'vs/base/common/arrays';
|
import { find } from 'vs/base/common/arrays';
|
||||||
|
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
|
||||||
|
|
||||||
interface IRawGalleryExtensionFile {
|
interface IRawGalleryExtensionFile {
|
||||||
assetType: string;
|
assetType: string;
|
||||||
@@ -388,7 +387,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||||||
@IConfigurationService private configurationService: IConfigurationService, // {{SQL CARBON EDIT}}
|
@IConfigurationService private configurationService: IConfigurationService, // {{SQL CARBON EDIT}}
|
||||||
@IFileService private readonly fileService: IFileService,
|
@IFileService private readonly fileService: IFileService,
|
||||||
@IProductService private readonly productService: IProductService,
|
@IProductService private readonly productService: IProductService,
|
||||||
@optional(IStorageService) private readonly storageService: IStorageService,
|
@IStorageService private readonly storageService: IStorageService,
|
||||||
) {
|
) {
|
||||||
const config = productService.extensionsGallery;
|
const config = productService.extensionsGallery;
|
||||||
this.extensionsGalleryUrl = config && config.serviceUrl;
|
this.extensionsGalleryUrl = config && config.serviceUrl;
|
||||||
@@ -926,43 +925,15 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService?: IStorageService): Promise<{ [key: string]: string; }> {
|
export async function resolveMarketplaceHeaders(version: string, environmentService: IEnvironmentService, fileService: IFileService, storageService: {
|
||||||
|
get: (key: string, scope: StorageScope) => string | undefined,
|
||||||
|
store: (key: string, value: string, scope: StorageScope) => void
|
||||||
|
}): Promise<{ [key: string]: string; }> {
|
||||||
const headers: IHeaders = {
|
const headers: IHeaders = {
|
||||||
'X-Market-Client-Id': `VSCode ${version}`,
|
'X-Market-Client-Id': `VSCode ${version}`,
|
||||||
'User-Agent': `VSCode ${version}`
|
'User-Agent': `VSCode ${version}`
|
||||||
};
|
};
|
||||||
let uuid: string | null = null;
|
const uuid: string = await getServiceMachineId(environmentService, fileService, storageService);
|
||||||
if (environmentService.galleryMachineIdResource) {
|
headers['X-Market-User-Id'] = uuid;
|
||||||
try {
|
|
||||||
const contents = await fileService.readFile(environmentService.galleryMachineIdResource);
|
|
||||||
const value = contents.value.toString();
|
|
||||||
uuid = isUUID(value) ? value : null;
|
|
||||||
} catch (e) {
|
|
||||||
uuid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!uuid) {
|
|
||||||
uuid = generateUuid();
|
|
||||||
try {
|
|
||||||
await fileService.writeFile(environmentService.galleryMachineIdResource, VSBuffer.fromString(uuid));
|
|
||||||
} catch (error) {
|
|
||||||
//noop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storageService) {
|
|
||||||
uuid = storageService.get('marketplace.userid', StorageScope.GLOBAL) || null;
|
|
||||||
if (!uuid) {
|
|
||||||
uuid = generateUuid();
|
|
||||||
storageService.store('marketplace.userid', uuid, StorageScope.GLOBAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uuid) {
|
|
||||||
headers['X-Market-User-Id'] = uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { NullLogService } from 'vs/platform/log/common/log';
|
|||||||
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import product from 'vs/platform/product/common/product';
|
import product from 'vs/platform/product/common/product';
|
||||||
|
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||||
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
|
|
||||||
suite('Extension Gallery Service', () => {
|
suite('Extension Gallery Service', () => {
|
||||||
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
|
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice');
|
||||||
@@ -52,11 +54,12 @@ suite('Extension Gallery Service', () => {
|
|||||||
test('marketplace machine id', () => {
|
test('marketplace machine id', () => {
|
||||||
const args = ['--user-data-dir', marketplaceHome];
|
const args = ['--user-data-dir', marketplaceHome];
|
||||||
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
|
const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath);
|
||||||
|
const storageService: IStorageService = new TestStorageService();
|
||||||
|
|
||||||
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers => {
|
return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers => {
|
||||||
assert.ok(isUUID(headers['X-Market-User-Id']));
|
assert.ok(isUUID(headers['X-Market-User-Id']));
|
||||||
|
|
||||||
return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers2 => {
|
return resolveMarketplaceHeaders(product.version, environmentService, fileService, storageService).then(headers2 => {
|
||||||
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
|
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||||
import { $ } from 'vs/base/browser/dom';
|
import { $, EventHelper, EventLike } from 'vs/base/browser/dom';
|
||||||
import { domEvent } from 'vs/base/browser/event';
|
import { domEvent } from 'vs/base/browser/event';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||||
@@ -46,9 +46,12 @@ export class Link extends Disposable {
|
|||||||
.map(e => new StandardKeyboardEvent(e))
|
.map(e => new StandardKeyboardEvent(e))
|
||||||
.filter(e => e.keyCode === KeyCode.Enter)
|
.filter(e => e.keyCode === KeyCode.Enter)
|
||||||
.event;
|
.event;
|
||||||
const onOpen = Event.any(onClick, onEnterPress);
|
const onOpen = Event.any<EventLike>(onClick, onEnterPress);
|
||||||
|
|
||||||
this._register(onOpen(_ => openerService.open(link.href)));
|
this._register(onOpen(e => {
|
||||||
|
EventHelper.stop(e, true);
|
||||||
|
openerService.open(link.href);
|
||||||
|
}));
|
||||||
|
|
||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ export const enum ProgressLocation {
|
|||||||
Extensions = 5,
|
Extensions = 5,
|
||||||
Window = 10,
|
Window = 10,
|
||||||
Notification = 15,
|
Notification = 15,
|
||||||
Dialog = 20,
|
Dialog = 20
|
||||||
View = 25
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProgressOptions {
|
export interface IProgressOptions {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/common/quickAccess';
|
import { PickerQuickAccessProvider, IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
|
||||||
import { distinct } from 'vs/base/common/arrays';
|
import { distinct } from 'vs/base/common/arrays';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
@@ -40,9 +40,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
|||||||
|
|
||||||
private static WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString);
|
private static WORD_FILTER = or(matchesPrefix, matchesWords, matchesContiguousSubString);
|
||||||
|
|
||||||
private readonly disposables = new DisposableStore();
|
private readonly commandsHistory = this._register(this.instantiationService.createInstance(CommandsHistory));
|
||||||
|
|
||||||
private readonly commandsHistory = this.disposables.add(this.instantiationService.createInstance(CommandsHistory));
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private options: ICommandsQuickAccessOptions,
|
private options: ICommandsQuickAccessOptions,
|
||||||
@@ -173,11 +171,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
|
|||||||
return commandPicks;
|
return commandPicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses to provide the actual command entries.
|
||||||
|
*/
|
||||||
protected abstract getCommandPicks(disposables: DisposableStore, token: CancellationToken): Promise<Array<ICommandQuickPick>>;
|
protected abstract getCommandPicks(disposables: DisposableStore, token: CancellationToken): Promise<Array<ICommandQuickPick>>;
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
this.disposables.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISerializedCommandHistory {
|
interface ISerializedCommandHistory {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
|||||||
// name of a provider (e.g. `?term` for terminals)
|
// name of a provider (e.g. `?term` for terminals)
|
||||||
disposables.add(picker.onDidChangeValue(value => {
|
disposables.add(picker.onDidChangeValue(value => {
|
||||||
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
||||||
if (providerDescriptor && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||||
this.quickInputService.quickAccess.show(providerDescriptor.prefix);
|
this.quickInputService.quickAccess.show(providerDescriptor.prefix);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||||
|
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||||
|
import { IQuickPickSeparator, IKeyMods, IQuickPickAcceptEvent } from 'vs/base/parts/quickinput/common/quickInput';
|
||||||
|
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
|
||||||
|
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
|
export enum TriggerAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing after the button was clicked.
|
||||||
|
*/
|
||||||
|
NO_ACTION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the picker.
|
||||||
|
*/
|
||||||
|
CLOSE_PICKER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the results of the picker.
|
||||||
|
*/
|
||||||
|
REFRESH_PICKER
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPickerQuickAccessItem extends IQuickPickItem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be executed when the pick item is accepted from
|
||||||
|
* the picker. The picker will close automatically before running this.
|
||||||
|
*
|
||||||
|
* @param keyMods the state of modifier keys when the item was accepted.
|
||||||
|
* @param event the underlying event that caused the accept to trigger.
|
||||||
|
*/
|
||||||
|
accept?(keyMods: IKeyMods, event: IQuickPickAcceptEvent): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method that will be executed when a button of the pick item was
|
||||||
|
* clicked on.
|
||||||
|
*
|
||||||
|
* @param buttonIndex index of the button of the item that
|
||||||
|
* was clicked.
|
||||||
|
*
|
||||||
|
* @param the state of modifier keys when the button was triggered.
|
||||||
|
*
|
||||||
|
* @returns a value that indicates what should happen after the trigger
|
||||||
|
* which can be a `Promise` for long running operations.
|
||||||
|
*/
|
||||||
|
trigger?(buttonIndex: number, keyMods: IKeyMods): TriggerAction | Promise<TriggerAction>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> extends Disposable implements IQuickAccessProvider {
|
||||||
|
|
||||||
|
constructor(private prefix: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
|
||||||
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
|
// Allow subclasses to configure picker
|
||||||
|
this.configure(picker);
|
||||||
|
|
||||||
|
// Disable filtering & sorting, we control the results
|
||||||
|
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||||
|
|
||||||
|
// Set initial picks and update on type
|
||||||
|
let picksCts: CancellationTokenSource | undefined = undefined;
|
||||||
|
const updatePickerItems = async () => {
|
||||||
|
|
||||||
|
// Cancel any previous ask for picks and busy
|
||||||
|
picksCts?.dispose(true);
|
||||||
|
picker.busy = false;
|
||||||
|
|
||||||
|
// Create new cancellation source for this run
|
||||||
|
picksCts = new CancellationTokenSource(token);
|
||||||
|
|
||||||
|
// Collect picks and support both long running and short
|
||||||
|
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), disposables.add(new DisposableStore()), picksCts.token);
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
picker.items = res;
|
||||||
|
} else {
|
||||||
|
picker.busy = true;
|
||||||
|
try {
|
||||||
|
const items = await res;
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
picker.items = items;
|
||||||
|
} finally {
|
||||||
|
if (!token.isCancellationRequested) {
|
||||||
|
picker.busy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
||||||
|
updatePickerItems();
|
||||||
|
|
||||||
|
// Accept the pick on accept and hide picker
|
||||||
|
disposables.add(picker.onDidAccept(event => {
|
||||||
|
const [item] = picker.selectedItems;
|
||||||
|
if (typeof item?.accept === 'function') {
|
||||||
|
if (!event.inBackground) {
|
||||||
|
picker.hide(); // hide picker unless we accept in background
|
||||||
|
}
|
||||||
|
item.accept(picker.keyMods, event);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Trigger the pick with button index if button triggered
|
||||||
|
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
|
||||||
|
if (typeof item.trigger === 'function') {
|
||||||
|
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
||||||
|
if (buttonIndex >= 0) {
|
||||||
|
const result = item.trigger(buttonIndex, picker.keyMods);
|
||||||
|
const action = (typeof result === 'number') ? result : await result;
|
||||||
|
|
||||||
|
if (token.isCancellationRequested) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case TriggerAction.NO_ACTION:
|
||||||
|
break;
|
||||||
|
case TriggerAction.CLOSE_PICKER:
|
||||||
|
picker.hide();
|
||||||
|
break;
|
||||||
|
case TriggerAction.REFRESH_PICKER:
|
||||||
|
updatePickerItems();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return disposables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses can override this method to configure the picker before showing it.
|
||||||
|
*
|
||||||
|
* @param picker the picker instance used for the quick access before it opens.
|
||||||
|
*/
|
||||||
|
protected configure(picker: IQuickPick<T>): void { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of picks and separators as needed. If the picks are resolved
|
||||||
|
* long running, the provided cancellation token should be used to cancel the
|
||||||
|
* operation when the token signals this.
|
||||||
|
*
|
||||||
|
* The implementor is responsible for filtering and sorting the picks given the
|
||||||
|
* provided `filter`.
|
||||||
|
*
|
||||||
|
* @param filter a filter to apply to the picks.
|
||||||
|
* @param disposables can be used to register disposables that should be cleaned
|
||||||
|
* up when the picker closes.
|
||||||
|
* @param token for long running tasks, implementors need to check on cancellation
|
||||||
|
* through this token.
|
||||||
|
*/
|
||||||
|
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
|
||||||
|
}
|
||||||
@@ -37,11 +37,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
// Create a picker for the provider to use with the initial value
|
// Create a picker for the provider to use with the initial value
|
||||||
// and adjust the filtering to exclude the prefix from filtering
|
// and adjust the filtering to exclude the prefix from filtering
|
||||||
const picker = disposables.add(this.quickInputService.createQuickPick());
|
const picker = disposables.add(this.quickInputService.createQuickPick());
|
||||||
picker.placeholder = descriptor.placeholder;
|
picker.placeholder = descriptor?.placeholder;
|
||||||
picker.value = value;
|
picker.value = value;
|
||||||
picker.valueSelection = [value.length, value.length];
|
picker.valueSelection = [value.length, value.length];
|
||||||
picker.contextKey = descriptor.contextKey;
|
picker.contextKey = descriptor?.contextKey;
|
||||||
picker.filterValue = (value: string) => value.substring(descriptor.prefix.length);
|
picker.filterValue = (value: string) => value.substring(descriptor ? descriptor.prefix.length : 0);
|
||||||
|
|
||||||
// Remember as last active picker and clean up once picker get's disposed
|
// Remember as last active picker and clean up once picker get's disposed
|
||||||
this.lastActivePicker = picker;
|
this.lastActivePicker = picker;
|
||||||
@@ -72,8 +72,10 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Ask provider to fill the picker as needed
|
// Ask provider to fill the picker as needed if we have one
|
||||||
disposables.add(provider.provide(picker, cts.token));
|
if (provider) {
|
||||||
|
disposables.add(provider.provide(picker, cts.token));
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, show the picker. This is important because a provider
|
// Finally, show the picker. This is important because a provider
|
||||||
// may not call this and then our disposables would leak that rely
|
// may not call this and then our disposables would leak that rely
|
||||||
@@ -81,8 +83,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
picker.show();
|
picker.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrInstantiateProvider(value: string): [IQuickAccessProvider, IQuickAccessProviderDescriptor] {
|
private getOrInstantiateProvider(value: string): [IQuickAccessProvider | undefined, IQuickAccessProviderDescriptor | undefined] {
|
||||||
const providerDescriptor = this.registry.getQuickAccessProvider(value) || this.registry.defaultProvider;
|
const providerDescriptor = this.registry.getQuickAccessProvider(value);
|
||||||
|
if (!providerDescriptor) {
|
||||||
|
return [undefined, undefined];
|
||||||
|
}
|
||||||
|
|
||||||
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
|
let provider = this.mapProviderToDescriptor.get(providerDescriptor);
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
|
|||||||
@@ -4,13 +4,11 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { first } from 'vs/base/common/arrays';
|
import { first, coalesce } from 'vs/base/common/arrays';
|
||||||
import { startsWith } from 'vs/base/common/strings';
|
import { startsWith } from 'vs/base/common/strings';
|
||||||
import { assertIsDefined } from 'vs/base/common/types';
|
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
|
||||||
import { IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
|
|
||||||
|
|
||||||
export interface IQuickAccessController {
|
export interface IQuickAccessController {
|
||||||
|
|
||||||
@@ -93,11 +91,6 @@ export const Extensions = {
|
|||||||
|
|
||||||
export interface IQuickAccessRegistry {
|
export interface IQuickAccessRegistry {
|
||||||
|
|
||||||
/**
|
|
||||||
* The default provider to use when no other provider matches.
|
|
||||||
*/
|
|
||||||
defaultProvider: IQuickAccessProviderDescriptor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a quick access provider to the platform.
|
* Registers a quick access provider to the platform.
|
||||||
*/
|
*/
|
||||||
@@ -114,172 +107,54 @@ export interface IQuickAccessRegistry {
|
|||||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
|
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuickAccessRegistry implements IQuickAccessRegistry {
|
export class QuickAccessRegistry implements IQuickAccessRegistry {
|
||||||
private providers: IQuickAccessProviderDescriptor[] = [];
|
private providers: IQuickAccessProviderDescriptor[] = [];
|
||||||
|
private defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
|
||||||
private _defaultProvider: IQuickAccessProviderDescriptor | undefined = undefined;
|
|
||||||
get defaultProvider(): IQuickAccessProviderDescriptor { return assertIsDefined(this._defaultProvider); }
|
|
||||||
set defaultProvider(provider: IQuickAccessProviderDescriptor) { this._defaultProvider = provider; }
|
|
||||||
|
|
||||||
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
|
registerQuickAccessProvider(provider: IQuickAccessProviderDescriptor): IDisposable {
|
||||||
this.providers.push(provider);
|
|
||||||
|
// Extract the default provider when no prefix is present
|
||||||
|
if (provider.prefix.length === 0) {
|
||||||
|
this.defaultProvider = provider;
|
||||||
|
} else {
|
||||||
|
this.providers.push(provider);
|
||||||
|
}
|
||||||
|
|
||||||
// sort the providers by decreasing prefix length, such that longer
|
// sort the providers by decreasing prefix length, such that longer
|
||||||
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
|
// prefixes take priority: 'ext' vs 'ext install' - the latter should win
|
||||||
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
|
this.providers.sort((providerA, providerB) => providerB.prefix.length - providerA.prefix.length);
|
||||||
|
|
||||||
return toDisposable(() => this.providers.splice(this.providers.indexOf(provider), 1));
|
return toDisposable(() => {
|
||||||
|
this.providers.splice(this.providers.indexOf(provider), 1);
|
||||||
|
|
||||||
|
if (this.defaultProvider === provider) {
|
||||||
|
this.defaultProvider = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
|
getQuickAccessProviders(): IQuickAccessProviderDescriptor[] {
|
||||||
return [this.defaultProvider, ...this.providers];
|
return coalesce([this.defaultProvider, ...this.providers]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
||||||
return prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
const result = prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
||||||
|
|
||||||
|
return result || this.defaultProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): Function {
|
||||||
|
const providers = [...this.providers];
|
||||||
|
const defaultProvider = this.defaultProvider;
|
||||||
|
|
||||||
|
this.providers = [];
|
||||||
|
this.defaultProvider = undefined;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.providers = providers;
|
||||||
|
this.defaultProvider = defaultProvider;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
|
Registry.add(Extensions.Quickaccess, new QuickAccessRegistry());
|
||||||
|
|
||||||
//#region Helper class for simple picker based providers
|
|
||||||
|
|
||||||
export enum TriggerAction {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do nothing after the button was clicked.
|
|
||||||
*/
|
|
||||||
NO_ACTION,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the picker.
|
|
||||||
*/
|
|
||||||
CLOSE_PICKER,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the results of the picker.
|
|
||||||
*/
|
|
||||||
REFRESH_PICKER
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IPickerQuickAccessItem extends IQuickPickItem {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that will be executed when the pick item is accepted from
|
|
||||||
* the picker. The picker will close automatically before running this.
|
|
||||||
*/
|
|
||||||
accept?(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method that will be executed when a button of the pick item was
|
|
||||||
* clicked on.
|
|
||||||
*
|
|
||||||
* @param buttonIndex index of the button of the item that
|
|
||||||
* was clicked.
|
|
||||||
*
|
|
||||||
* @returns a value that indicates what should happen after the trigger
|
|
||||||
* which can be a `Promise` for long running operations.
|
|
||||||
*/
|
|
||||||
trigger?(buttonIndex: number): TriggerAction | Promise<TriggerAction>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem> implements IQuickAccessProvider {
|
|
||||||
|
|
||||||
constructor(private prefix: string) { }
|
|
||||||
|
|
||||||
provide(picker: IQuickPick<T>, token: CancellationToken): IDisposable {
|
|
||||||
const disposables = new DisposableStore();
|
|
||||||
|
|
||||||
// Disable filtering & sorting, we control the results
|
|
||||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
|
||||||
|
|
||||||
// Set initial picks and update on type
|
|
||||||
let picksCts: CancellationTokenSource | undefined = undefined;
|
|
||||||
const updatePickerItems = async () => {
|
|
||||||
|
|
||||||
// Cancel any previous ask for picks and busy
|
|
||||||
picksCts?.dispose(true);
|
|
||||||
picker.busy = false;
|
|
||||||
|
|
||||||
// Create new cancellation source for this run
|
|
||||||
picksCts = new CancellationTokenSource(token);
|
|
||||||
|
|
||||||
// Collect picks and support both long running and short
|
|
||||||
const res = this.getPicks(picker.value.substr(this.prefix.length).trim(), disposables.add(new DisposableStore()), picksCts.token);
|
|
||||||
if (Array.isArray(res)) {
|
|
||||||
picker.items = res;
|
|
||||||
} else {
|
|
||||||
picker.busy = true;
|
|
||||||
try {
|
|
||||||
const items = await res;
|
|
||||||
if (token.isCancellationRequested) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
picker.items = items;
|
|
||||||
} finally {
|
|
||||||
if (!token.isCancellationRequested) {
|
|
||||||
picker.busy = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
|
|
||||||
updatePickerItems();
|
|
||||||
|
|
||||||
// Accept the pick on accept and hide picker
|
|
||||||
disposables.add(picker.onDidAccept(() => {
|
|
||||||
const [item] = picker.selectedItems;
|
|
||||||
if (typeof item?.accept === 'function') {
|
|
||||||
picker.hide();
|
|
||||||
item.accept();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Trigger the pick with button index if button triggered
|
|
||||||
disposables.add(picker.onDidTriggerItemButton(async ({ button, item }) => {
|
|
||||||
if (typeof item.trigger === 'function') {
|
|
||||||
const buttonIndex = item.buttons?.indexOf(button) ?? -1;
|
|
||||||
if (buttonIndex >= 0) {
|
|
||||||
const result = item.trigger(buttonIndex);
|
|
||||||
const action = (typeof result === 'number') ? result : await result;
|
|
||||||
|
|
||||||
if (token.isCancellationRequested) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case TriggerAction.NO_ACTION:
|
|
||||||
break;
|
|
||||||
case TriggerAction.CLOSE_PICKER:
|
|
||||||
picker.hide();
|
|
||||||
break;
|
|
||||||
case TriggerAction.REFRESH_PICKER:
|
|
||||||
updatePickerItems();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
return disposables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of picks and separators as needed. If the picks are resolved
|
|
||||||
* long running, the provided cancellation token should be used to cancel the
|
|
||||||
* operation when the token signals this.
|
|
||||||
*
|
|
||||||
* The implementor is responsible for filtering and sorting the picks given the
|
|
||||||
* provided `filter`.
|
|
||||||
*
|
|
||||||
* @param filter a filter to apply to the picks.
|
|
||||||
* @param disposables can be used to register disposables that should be cleaned
|
|
||||||
* up when the picker closes.
|
|
||||||
* @param token for long running tasks, implementors need to check on cancellation
|
|
||||||
* through this token.
|
|
||||||
*/
|
|
||||||
protected abstract getPicks(filter: string, disposables: DisposableStore, token: CancellationToken): Array<T | IQuickPickSeparator> | Promise<Array<T | IQuickPickSeparator>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
|
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
|
import { isUUID, generateUuid } from 'vs/base/common/uuid';
|
||||||
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
|
|
||||||
|
export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: {
|
||||||
|
get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined,
|
||||||
|
store: (key: string, value: string, scope: StorageScope) => void
|
||||||
|
}): Promise<string> {
|
||||||
|
let uuid: string | null = storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null;
|
||||||
|
if (uuid) {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
if (environmentService.serviceMachineIdResource) {
|
||||||
|
try {
|
||||||
|
const contents = await fileService.readFile(environmentService.serviceMachineIdResource);
|
||||||
|
const value = contents.value.toString();
|
||||||
|
uuid = isUUID(value) ? value : null;
|
||||||
|
} catch (e) {
|
||||||
|
uuid = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uuid) {
|
||||||
|
uuid = generateUuid();
|
||||||
|
try {
|
||||||
|
await fileService.writeFile(environmentService.serviceMachineIdResource, VSBuffer.fromString(uuid));
|
||||||
|
} catch (error) {
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uuid = generateUuid();
|
||||||
|
}
|
||||||
|
storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL);
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
@@ -112,6 +112,11 @@ export interface IColorTheme {
|
|||||||
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
|
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
|
||||||
*/
|
*/
|
||||||
readonly tokenColorMap: string[];
|
readonly tokenColorMap: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines whether semantic highlighting should be enabled for the theme.
|
||||||
|
*/
|
||||||
|
readonly semanticHighlighting: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFileIconTheme {
|
export interface IFileIconTheme {
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ export class TestColorTheme implements IColorTheme {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly semanticHighlighting = false;
|
||||||
|
|
||||||
get tokenColorMap(): string[] {
|
get tokenColorMap(): string[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||||
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { parse } from 'vs/base/common/json';
|
import { parse } from 'vs/base/common/json';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
@@ -33,7 +33,7 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent {
|
|||||||
&& Object.keys(thing).length === 1;
|
&& Object.keys(thing).length === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements ISettingsSyncService {
|
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
|
||||||
|
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
|
|
||||||
|
|||||||
@@ -333,14 +333,6 @@ export interface IConflictSetting {
|
|||||||
remoteValue: any | undefined;
|
remoteValue: any | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
|
|
||||||
export interface ISettingsSyncService extends IUserDataSynchroniser {
|
|
||||||
_serviceBrand: any;
|
|
||||||
readonly onDidChangeConflicts: Event<IConflictSetting[]>;
|
|
||||||
readonly conflicts: IConflictSetting[];
|
|
||||||
resolveSettingsConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);
|
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
|
import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
|
|
||||||
type SyncEnablementClassification = {
|
type SyncEnablementClassification = {
|
||||||
enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
enabled?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||||
@@ -29,8 +30,17 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa
|
|||||||
constructor(
|
constructor(
|
||||||
@IStorageService private readonly storageService: IStorageService,
|
@IStorageService private readonly storageService: IStorageService,
|
||||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||||
|
@IEnvironmentService environmentService: IEnvironmentService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
switch (environmentService.args['sync']) {
|
||||||
|
case 'on':
|
||||||
|
this.setEnablement(true);
|
||||||
|
break;
|
||||||
|
case 'off':
|
||||||
|
this.setEnablement(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
|
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { IStringDictionary } from 'vs/base/common/collections';
|
import { IStringDictionary } from 'vs/base/common/collections';
|
||||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||||
@@ -41,40 +41,6 @@ export class UserDataSyncChannel implements IServerChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SettingsSyncChannel implements IServerChannel {
|
|
||||||
|
|
||||||
constructor(private readonly service: ISettingsSyncService) { }
|
|
||||||
|
|
||||||
listen(_: unknown, event: string): Event<any> {
|
|
||||||
switch (event) {
|
|
||||||
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
|
|
||||||
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
|
|
||||||
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
|
|
||||||
}
|
|
||||||
throw new Error(`Event not found: ${event}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
call(context: any, command: string, args?: any): Promise<any> {
|
|
||||||
switch (command) {
|
|
||||||
case 'sync': return this.service.sync();
|
|
||||||
case 'accept': return this.service.accept(args[0]);
|
|
||||||
case 'pull': return this.service.pull();
|
|
||||||
case 'push': return this.service.push();
|
|
||||||
case '_getInitialStatus': return Promise.resolve(this.service.status);
|
|
||||||
case '_getInitialConflicts': return Promise.resolve(this.service.conflicts);
|
|
||||||
case 'stop': this.service.stop(); return Promise.resolve();
|
|
||||||
case 'resetLocal': return this.service.resetLocal();
|
|
||||||
case 'hasPreviouslySynced': return this.service.hasPreviouslySynced();
|
|
||||||
case 'hasLocalData': return this.service.hasLocalData();
|
|
||||||
case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]);
|
|
||||||
case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview();
|
|
||||||
case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]);
|
|
||||||
case 'getLocalBackupContent': return this.service.getLocalBackupContent(args[0], args[1]);
|
|
||||||
}
|
|
||||||
throw new Error('Invalid call');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserDataAutoSyncChannel implements IServerChannel {
|
export class UserDataAutoSyncChannel implements IServerChannel {
|
||||||
|
|
||||||
constructor(private readonly service: IUserDataAutoSyncService) { }
|
constructor(private readonly service: IUserDataAutoSyncService) { }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, ISettingsSyncService, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
|
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveSyncResource, PREVIEW_QUERY } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { Emitter, Event } from 'vs/base/common/event';
|
import { Emitter, Event } from 'vs/base/common/event';
|
||||||
@@ -16,6 +16,7 @@ import { equals } from 'vs/base/common/arrays';
|
|||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
|
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
||||||
|
|
||||||
type SyncErrorClassification = {
|
type SyncErrorClassification = {
|
||||||
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||||
@@ -51,6 +52,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||||||
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
|
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
|
||||||
readonly onDidChangeLastSyncTime: Event<number> = this._onDidChangeLastSyncTime.event;
|
readonly onDidChangeLastSyncTime: Event<number> = this._onDidChangeLastSyncTime.event;
|
||||||
|
|
||||||
|
private readonly settingsSynchroniser: SettingsSynchroniser;
|
||||||
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
|
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
|
||||||
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
|
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
|
||||||
private readonly globalStateSynchroniser: GlobalStateSynchroniser;
|
private readonly globalStateSynchroniser: GlobalStateSynchroniser;
|
||||||
@@ -58,12 +60,12 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
|||||||
constructor(
|
constructor(
|
||||||
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
||||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||||
@ISettingsSyncService private readonly settingsSynchroniser: ISettingsSyncService,
|
|
||||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||||
@IStorageService private readonly storageService: IStorageService,
|
@IStorageService private readonly storageService: IStorageService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
|
||||||
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
|
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
|
||||||
this.globalStateSynchroniser = this._register(this.instantiationService.createInstance(GlobalStateSynchroniser));
|
this.globalStateSynchroniser = this._register(this.instantiationService.createInstance(GlobalStateSynchroniser));
|
||||||
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
|
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
|
||||||
|
|||||||
@@ -13,12 +13,19 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||||
import { IProductService } from 'vs/platform/product/common/productService';
|
import { IProductService } from 'vs/platform/product/common/productService';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
|
import { getServiceMachineId } from 'vs/platform/serviceMachineId/common/serviceMachineId';
|
||||||
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
|
import { assign } from 'vs/base/common/objects';
|
||||||
|
|
||||||
|
|
||||||
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
|
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
|
||||||
|
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
|
|
||||||
readonly userDataSyncStore: IUserDataSyncStore | undefined;
|
readonly userDataSyncStore: IUserDataSyncStore | undefined;
|
||||||
|
private readonly commonHeadersPromise: Promise<{ [key: string]: string; }>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IProductService productService: IProductService,
|
@IProductService productService: IProductService,
|
||||||
@@ -26,9 +33,17 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
|
|||||||
@IRequestService private readonly requestService: IRequestService,
|
@IRequestService private readonly requestService: IRequestService,
|
||||||
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
|
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
|
||||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||||
|
@IEnvironmentService environmentService: IEnvironmentService,
|
||||||
|
@IFileService fileService: IFileService,
|
||||||
|
@IStorageService storageService: IStorageService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
|
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
|
||||||
|
this.commonHeadersPromise = getServiceMachineId(environmentService, fileService, storageService)
|
||||||
|
.then(uuid => ({
|
||||||
|
'X-Sync-Client-Id': productService.version,
|
||||||
|
'X-Sync-Machine-Id': uuid
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllRefs(resource: SyncResource): Promise<IResourceRefHandle[]> {
|
async getAllRefs(resource: SyncResource): Promise<IResourceRefHandle[]> {
|
||||||
@@ -46,7 +61,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await asJson<{ url: string, created: number }[]>(context) || [];
|
const result = await asJson<{ url: string, created: number }[]>(context) || [];
|
||||||
return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url))!, created: created }));
|
return result.map(({ url, created }) => ({ ref: relativePath(uri, URI.parse(url).with({ scheme: uri.scheme, authority: uri.authority }))!, created: created * 1000 /* Server returns in seconds */ }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveContent(resource: SyncResource, ref: string): Promise<string | null> {
|
async resolveContent(resource: SyncResource, ref: string): Promise<string | null> {
|
||||||
@@ -174,8 +189,11 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
|
|||||||
if (!authToken) {
|
if (!authToken) {
|
||||||
throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, source);
|
throw new UserDataSyncStoreError('No Auth Token Available', UserDataSyncErrorCode.Unauthorized, source);
|
||||||
}
|
}
|
||||||
options.headers = options.headers || {};
|
|
||||||
options.headers['authorization'] = `Bearer ${authToken}`;
|
const commonHeaders = await this.commonHeadersPromise;
|
||||||
|
options.headers = assign(options.headers || {}, commonHeaders, {
|
||||||
|
'authorization': `Bearer ${authToken}`,
|
||||||
|
});
|
||||||
|
|
||||||
this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } });
|
this.logService.trace('Sending request to server', { url: options.url, type: options.type, headers: { ...options.headers, ...{ authorization: undefined } } });
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { IRequestService } from 'vs/platform/request/common/request';
|
import { IRequestService } from 'vs/platform/request/common/request';
|
||||||
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
|
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
|
import { IUserData, IUserDataManifest, ALL_SYNC_RESOURCES, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
|
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
|
||||||
@@ -31,7 +31,6 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen
|
|||||||
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
|
||||||
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
import { ConfigurationService } from 'vs/platform/configuration/common/configurationService';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
|
||||||
import { Emitter } from 'vs/base/common/event';
|
import { Emitter } from 'vs/base/common/event';
|
||||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||||
import product from 'vs/platform/product/common/product';
|
import product from 'vs/platform/product/common/product';
|
||||||
@@ -57,6 +56,7 @@ export class UserDataSyncClient extends Disposable {
|
|||||||
keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'),
|
keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'),
|
||||||
keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'),
|
keybindingsSyncPreviewResource: joinPath(userDataSyncHome, 'keybindings.json'),
|
||||||
argvResource: joinPath(userDataDirectory, 'argv.json'),
|
argvResource: joinPath(userDataDirectory, 'argv.json'),
|
||||||
|
args: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
const logService = new NullLogService();
|
const logService = new NullLogService();
|
||||||
@@ -105,7 +105,6 @@ export class UserDataSyncClient extends Disposable {
|
|||||||
async getCompatibleExtension() { return null; }
|
async getCompatibleExtension() { return null; }
|
||||||
});
|
});
|
||||||
|
|
||||||
this.instantiationService.stub(ISettingsSyncService, this.instantiationService.createInstance(SettingsSynchroniser));
|
|
||||||
this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService));
|
this.instantiationService.stub(IUserDataSyncService, this.instantiationService.createInstance(UserDataSyncService));
|
||||||
|
|
||||||
if (!empty) {
|
if (!empty) {
|
||||||
|
|||||||
Vendored
+6
-3
@@ -2116,6 +2116,9 @@ declare module 'vscode' {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A [command](#Command) this code action executes.
|
* A [command](#Command) this code action executes.
|
||||||
|
*
|
||||||
|
* If this command throws an exception, VS Code displays the exception message to users in the editor at the
|
||||||
|
* current cursor position.
|
||||||
*/
|
*/
|
||||||
command?: Command;
|
command?: Command;
|
||||||
|
|
||||||
@@ -2145,8 +2148,8 @@ declare module 'vscode' {
|
|||||||
* of code action, such as refactorings.
|
* of code action, such as refactorings.
|
||||||
*
|
*
|
||||||
* - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions)
|
* - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions)
|
||||||
* that auto applies a code action and only a disabled code actions are returned, VS Code will show the user a
|
* that auto applies a code action and only a disabled code actions are returned, VS Code will show the user an
|
||||||
* message with `reason` in the editor.
|
* error message with `reason` in the editor.
|
||||||
*/
|
*/
|
||||||
disabled?: {
|
disabled?: {
|
||||||
/**
|
/**
|
||||||
@@ -7929,7 +7932,7 @@ declare module 'vscode' {
|
|||||||
/**
|
/**
|
||||||
* The location at which progress should show.
|
* The location at which progress should show.
|
||||||
*/
|
*/
|
||||||
location: ProgressLocation;
|
location: ProgressLocation | { viewId: string };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A human-readable string which will be used to describe the
|
* A human-readable string which will be used to describe the
|
||||||
|
|||||||
Vendored
+31
-19
@@ -1086,6 +1086,22 @@ declare module 'vscode' {
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606
|
||||||
|
|
||||||
|
export namespace window {
|
||||||
|
export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TerminalLinkHandler {
|
||||||
|
/**
|
||||||
|
* @return true when the link was handled (and should not be considered by
|
||||||
|
* other providers including the default), false when the link was not handled.
|
||||||
|
*/
|
||||||
|
handleLink(terminal: Terminal, link: string): ProviderResult<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
//#region Joh -> exclusive document filters
|
//#region Joh -> exclusive document filters
|
||||||
|
|
||||||
export interface DocumentFilter {
|
export interface DocumentFilter {
|
||||||
@@ -1457,6 +1473,21 @@ declare module 'vscode' {
|
|||||||
* @return Thenable indicating that the webview editor has been resolved.
|
* @return Thenable indicating that the webview editor has been resolved.
|
||||||
*/
|
*/
|
||||||
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable<void>;
|
resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel): Thenable<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: discuss this at api sync.
|
||||||
|
*
|
||||||
|
* Handle when the underlying resource for a custom editor is renamed.
|
||||||
|
*
|
||||||
|
* This allows the webview for the editor be preserved throughout the rename. If this method is not implemented,
|
||||||
|
* VS Code will destory the previous custom editor and create a replacement one.
|
||||||
|
*
|
||||||
|
* @param newDocument New text document to use for the custom editor.
|
||||||
|
* @param existingWebviewPanel Webview panel for the custom editor.
|
||||||
|
*
|
||||||
|
* @return Thenable indicating that the webview editor has been moved.
|
||||||
|
*/
|
||||||
|
moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel): Thenable<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace window {
|
namespace window {
|
||||||
@@ -1860,23 +1891,4 @@ declare module 'vscode' {
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region https://github.com/microsoft/vscode/issues/92421
|
|
||||||
|
|
||||||
export enum ProgressLocation {
|
|
||||||
/**
|
|
||||||
* Show progress for a view, as progress bar inside the view (when visible),
|
|
||||||
* and as an overlay on the activity bar icon. Doesn't support cancellation or discrete progress.
|
|
||||||
*/
|
|
||||||
View = 25,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProgressOptions {
|
|
||||||
/**
|
|
||||||
* The target view identifier for showing progress when using [ProgressLocation.View](#ProgressLocation.View).
|
|
||||||
*/
|
|
||||||
viewId?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal';
|
import { IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||||
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
|
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||||
import { ITerminalInstanceService, ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||||
@@ -23,6 +23,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||||||
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
|
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
|
||||||
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
|
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
|
||||||
private _dataEventTracker: TerminalDataEventTracker | undefined;
|
private _dataEventTracker: TerminalDataEventTracker | undefined;
|
||||||
|
private _linkHandler: IDisposable | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
extHostContext: IExtHostContext,
|
extHostContext: IExtHostContext,
|
||||||
@@ -146,6 +147,22 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public $startHandlingLinks(): void {
|
||||||
|
this._linkHandler?.dispose();
|
||||||
|
this._linkHandler = this._terminalService.addLinkHandler(this._remoteAuthority || '', e => this._handleLink(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public $stopHandlingLinks(): void {
|
||||||
|
this._linkHandler?.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise<boolean> {
|
||||||
|
if (!e.terminal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this._proxy.$handleLink(e.terminal.id, e.link);
|
||||||
|
}
|
||||||
|
|
||||||
private _onActiveTerminalChanged(terminalId: number | null): void {
|
private _onActiveTerminalChanged(terminalId: number | null): void {
|
||||||
this._proxy.$acceptActiveTerminalChanged(terminalId);
|
this._proxy.$acceptActiveTerminalChanged(terminalId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,12 +279,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
this._revivers.delete(viewType);
|
this._revivers.delete(viewType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
|
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||||
return this.registerEditorProvider(ModelType.Text, extensionData, viewType, options);
|
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
|
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void {
|
||||||
return this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options);
|
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerEditorProvider(
|
private registerEditorProvider(
|
||||||
@@ -292,41 +292,45 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||||
viewType: string,
|
viewType: string,
|
||||||
options: modes.IWebviewPanelOptions,
|
options: modes.IWebviewPanelOptions,
|
||||||
): void {
|
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||||
|
): DisposableStore {
|
||||||
if (this._editorProviders.has(viewType)) {
|
if (this._editorProviders.has(viewType)) {
|
||||||
throw new Error(`Provider for ${viewType} already registered`);
|
throw new Error(`Provider for ${viewType} already registered`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extension = reviveWebviewExtension(extensionData);
|
const extension = reviveWebviewExtension(extensionData);
|
||||||
|
|
||||||
this._editorProviders.set(viewType, this._webviewWorkbenchService.registerResolver({
|
const disposables = new DisposableStore();
|
||||||
|
disposables.add(this._webviewWorkbenchService.registerResolver({
|
||||||
canResolve: (webviewInput) => {
|
canResolve: (webviewInput) => {
|
||||||
return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType;
|
return webviewInput instanceof CustomEditorInput && webviewInput.viewType === viewType;
|
||||||
},
|
},
|
||||||
resolveWebview: async (webviewInput: CustomEditorInput) => {
|
resolveWebview: async (webviewInput: CustomEditorInput) => {
|
||||||
const handle = webviewInput.id;
|
const handle = webviewInput.id;
|
||||||
this._webviewInputs.add(handle, webviewInput);
|
this._webviewInputs.add(handle, webviewInput);
|
||||||
this.hookupWebviewEventDelegate(handle, webviewInput);
|
|
||||||
|
|
||||||
|
this.hookupWebviewEventDelegate(handle, webviewInput);
|
||||||
webviewInput.webview.options = options;
|
webviewInput.webview.options = options;
|
||||||
webviewInput.webview.extension = extension;
|
webviewInput.webview.extension = extension;
|
||||||
|
|
||||||
const resource = webviewInput.resource;
|
const resource = webviewInput.resource;
|
||||||
|
let modelRef = await this.getOrCreateCustomEditorModel(modelType, resource, viewType);
|
||||||
|
|
||||||
const modelRef = await this.getOrCreateCustomEditorModel(modelType, webviewInput, resource, viewType);
|
|
||||||
webviewInput.webview.onDispose(() => {
|
webviewInput.webview.onDispose(() => {
|
||||||
modelRef.dispose();
|
modelRef.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (capabilities.supportsMove) {
|
||||||
|
webviewInput.onMove(async (newResource: URI) => {
|
||||||
|
const oldModel = modelRef;
|
||||||
|
modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType);
|
||||||
|
this._proxy.$onMoveCustomEditor(handle, newResource, viewType);
|
||||||
|
oldModel.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._proxy.$resolveWebviewEditor(
|
await this._proxy.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
|
||||||
resource,
|
|
||||||
handle,
|
|
||||||
viewType,
|
|
||||||
webviewInput.getTitle(),
|
|
||||||
editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0),
|
|
||||||
webviewInput.webview.options
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onUnexpectedError(error);
|
onUnexpectedError(error);
|
||||||
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
|
webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
|
||||||
@@ -334,6 +338,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._editorProviders.set(viewType, disposables);
|
||||||
|
|
||||||
|
return disposables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public $unregisterEditorProvider(viewType: string): void {
|
public $unregisterEditorProvider(viewType: string): void {
|
||||||
@@ -350,11 +358,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
|
|
||||||
private async getOrCreateCustomEditorModel(
|
private async getOrCreateCustomEditorModel(
|
||||||
modelType: ModelType,
|
modelType: ModelType,
|
||||||
webviewInput: WebviewInput,
|
|
||||||
resource: URI,
|
resource: URI,
|
||||||
viewType: string,
|
viewType: string,
|
||||||
): Promise<IReference<ICustomEditorModel>> {
|
): Promise<IReference<ICustomEditorModel>> {
|
||||||
const existingModel = this._customEditorService.models.tryRetain(webviewInput.resource, webviewInput.viewType);
|
const existingModel = this._customEditorService.models.tryRetain(resource, viewType);
|
||||||
if (existingModel) {
|
if (existingModel) {
|
||||||
return existingModel;
|
return existingModel;
|
||||||
}
|
}
|
||||||
@@ -383,14 +390,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||||||
disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
|
disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
|
||||||
disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
|
disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
|
||||||
|
|
||||||
input.onDispose(() => {
|
disposables.add(input.webview.onDispose(() => {
|
||||||
disposables.dispose();
|
disposables.dispose();
|
||||||
});
|
|
||||||
input.webview.onDispose(() => {
|
|
||||||
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
|
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
|
||||||
this._webviewInputs.delete(handle);
|
this._webviewInputs.delete(handle);
|
||||||
});
|
});
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
|
private registerWebviewFromDiffEditorListeners(diffEditorInput: DiffEditorInput): void {
|
||||||
@@ -536,6 +542,8 @@ namespace HotExitState {
|
|||||||
export type State = typeof Allowed | typeof NotAllowed | Pending;
|
export type State = typeof Allowed | typeof NotAllowed | Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customDocumentFileScheme = 'custom';
|
||||||
|
|
||||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||||
|
|
||||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||||
@@ -556,7 +564,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
|
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
|
||||||
private readonly _viewType: string,
|
private readonly _viewType: string,
|
||||||
private readonly _resource: URI,
|
private readonly _realResource: URI,
|
||||||
private readonly _editable: boolean,
|
private readonly _editable: boolean,
|
||||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||||
@ILabelService private readonly _labelService: ILabelService,
|
@ILabelService private readonly _labelService: ILabelService,
|
||||||
@@ -564,6 +572,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (_editable) {
|
if (_editable) {
|
||||||
this._register(workingCopyService.registerWorkingCopy(this));
|
this._register(workingCopyService.registerWorkingCopy(this));
|
||||||
}
|
}
|
||||||
@@ -571,18 +580,26 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
if (this._editable) {
|
if (this._editable) {
|
||||||
this._undoService.removeElements(this.resource);
|
this._undoService.removeElements(this._realResource);
|
||||||
}
|
}
|
||||||
this._proxy.$disposeWebviewCustomEditorDocument(this.resource, this._viewType);
|
this._proxy.$disposeWebviewCustomEditorDocument(this._realResource, this._viewType);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region IWorkingCopy
|
//#region IWorkingCopy
|
||||||
|
|
||||||
public get resource() { return this._resource; } // custom://viewType/path/file
|
public get resource() {
|
||||||
|
// Make sure each custom editor has a unique resource for backup and edits
|
||||||
|
return URI.from({
|
||||||
|
scheme: customDocumentFileScheme,
|
||||||
|
authority: this._viewType,
|
||||||
|
path: this._realResource.path,
|
||||||
|
query: JSON.stringify(this._realResource.toJSON())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public get name() {
|
public get name() {
|
||||||
return basename(this._labelService.getUriLabel(this._resource));
|
return basename(this._labelService.getUriLabel(this._realResource));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get capabilities(): WorkingCopyCapabilities {
|
public get capabilities(): WorkingCopyCapabilities {
|
||||||
@@ -601,6 +618,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
public isReadonly() {
|
||||||
|
return this._editable;
|
||||||
|
}
|
||||||
|
|
||||||
public get viewType() {
|
public get viewType() {
|
||||||
return this._viewType;
|
return this._viewType;
|
||||||
}
|
}
|
||||||
@@ -617,7 +638,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
|
|
||||||
this._undoService.pushElement({
|
this._undoService.pushElement({
|
||||||
type: UndoRedoElementType.Resource,
|
type: UndoRedoElementType.Resource,
|
||||||
resource: this.resource,
|
resource: this._realResource,
|
||||||
label: label ?? localize('defaultEditLabel', "Edit"),
|
label: label ?? localize('defaultEditLabel', "Edit"),
|
||||||
undo: () => this.undo(),
|
undo: () => this.undo(),
|
||||||
redo: () => this.redo(),
|
redo: () => this.redo(),
|
||||||
@@ -635,7 +656,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
const undoneEdit = this._edits[this._currentEditIndex];
|
const undoneEdit = this._edits[this._currentEditIndex];
|
||||||
await this._proxy.$undo(this.resource, this.viewType, undoneEdit);
|
await this._proxy.$undo(this._realResource, this.viewType, undoneEdit);
|
||||||
|
|
||||||
this.change(() => {
|
this.change(() => {
|
||||||
--this._currentEditIndex;
|
--this._currentEditIndex;
|
||||||
@@ -653,7 +674,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
}
|
}
|
||||||
|
|
||||||
const redoneEdit = this._edits[this._currentEditIndex + 1];
|
const redoneEdit = this._edits[this._currentEditIndex + 1];
|
||||||
await this._proxy.$redo(this.resource, this.viewType, redoneEdit);
|
await this._proxy.$redo(this._realResource, this.viewType, redoneEdit);
|
||||||
this.change(() => {
|
this.change(() => {
|
||||||
++this._currentEditIndex;
|
++this._currentEditIndex;
|
||||||
});
|
});
|
||||||
@@ -668,7 +689,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
: this._edits.splice(start, toRemove);
|
: this._edits.splice(start, toRemove);
|
||||||
|
|
||||||
if (removedEdits.length) {
|
if (removedEdits.length) {
|
||||||
this._proxy.$disposeEdits(this.resource, this._viewType, removedEdits);
|
this._proxy.$disposeEdits(this._realResource, this._viewType, removedEdits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +721,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
|
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._proxy.$revert(this.resource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo });
|
this._proxy.$revert(this._realResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo });
|
||||||
this.change(() => {
|
this.change(() => {
|
||||||
this._currentEditIndex = this._savePoint;
|
this._currentEditIndex = this._savePoint;
|
||||||
this.spliceEdits();
|
this.spliceEdits();
|
||||||
@@ -711,7 +732,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
if (!this._editable) {
|
if (!this._editable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await createCancelablePromise(token => this._proxy.$onSave(this.resource, this.viewType, token));
|
await createCancelablePromise(token => this._proxy.$onSave(this._realResource, this.viewType, token));
|
||||||
this.change(() => {
|
this.change(() => {
|
||||||
this._savePoint = this._currentEditIndex;
|
this._savePoint = this._currentEditIndex;
|
||||||
});
|
});
|
||||||
@@ -720,7 +741,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
|
|
||||||
public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
|
public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
|
||||||
if (this._editable) {
|
if (this._editable) {
|
||||||
await this._proxy.$onSaveAs(this.resource, this.viewType, targetResource);
|
await this._proxy.$onSaveAs(this._realResource, this.viewType, targetResource);
|
||||||
this.change(() => {
|
this.change(() => {
|
||||||
this._savePoint = this._currentEditIndex;
|
this._savePoint = this._currentEditIndex;
|
||||||
});
|
});
|
||||||
@@ -749,7 +770,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||||||
|
|
||||||
const pendingState = new HotExitState.Pending(
|
const pendingState = new HotExitState.Pending(
|
||||||
createCancelablePromise(token =>
|
createCancelablePromise(token =>
|
||||||
this._proxy.$backup(this.resource.toJSON(), this.viewType, token)));
|
this._proxy.$backup(this._realResource.toJSON(), this.viewType, token)));
|
||||||
this._hotExitState = pendingState;
|
this._hotExitState = pendingState;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||||||
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
|
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
|
||||||
},
|
},
|
||||||
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
|
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
|
||||||
if (options.location === extHostTypes.ProgressLocation.View) {
|
if (typeof options.location === 'object') {
|
||||||
checkProposedApiEnabled(extension);
|
checkProposedApiEnabled(extension);
|
||||||
}
|
}
|
||||||
return extHostProgress.withProgress(extension, options, task);
|
return extHostProgress.withProgress(extension, options, task);
|
||||||
@@ -567,6 +567,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||||||
}
|
}
|
||||||
return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs);
|
return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs);
|
||||||
},
|
},
|
||||||
|
registerTerminalLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable {
|
||||||
|
checkProposedApiEnabled(extension);
|
||||||
|
return extHostTerminalService.registerLinkHandler(handler);
|
||||||
|
},
|
||||||
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
|
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
|
||||||
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
|
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export interface IEnvironment {
|
|||||||
userHome: URI;
|
userHome: URI;
|
||||||
webviewResourceRoot: string;
|
webviewResourceRoot: string;
|
||||||
webviewCspSource: string;
|
webviewCspSource: string;
|
||||||
|
useHostProxy?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStaticWorkspaceData {
|
export interface IStaticWorkspaceData {
|
||||||
@@ -435,6 +436,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
|
|||||||
$show(terminalId: number, preserveFocus: boolean): void;
|
$show(terminalId: number, preserveFocus: boolean): void;
|
||||||
$startSendingDataEvents(): void;
|
$startSendingDataEvents(): void;
|
||||||
$stopSendingDataEvents(): void;
|
$stopSendingDataEvents(): void;
|
||||||
|
$startHandlingLinks(): void;
|
||||||
|
$stopHandlingLinks(): void;
|
||||||
|
|
||||||
// Process
|
// Process
|
||||||
$sendProcessTitle(terminalId: number, title: string): void;
|
$sendProcessTitle(terminalId: number, title: string): void;
|
||||||
@@ -577,6 +580,10 @@ export interface WebviewExtensionDescription {
|
|||||||
readonly location: UriComponents;
|
readonly location: UriComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CustomTextEditorCapabilities {
|
||||||
|
readonly supportsMove?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MainThreadWebviewsShape extends IDisposable {
|
export interface MainThreadWebviewsShape extends IDisposable {
|
||||||
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
|
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
|
||||||
$disposeWebview(handle: WebviewPanelHandle): void;
|
$disposeWebview(handle: WebviewPanelHandle): void;
|
||||||
@@ -592,7 +599,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
|
|||||||
$registerSerializer(viewType: string): void;
|
$registerSerializer(viewType: string): void;
|
||||||
$unregisterSerializer(viewType: string): void;
|
$unregisterSerializer(viewType: string): void;
|
||||||
|
|
||||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
|
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
|
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void;
|
||||||
$unregisterEditorProvider(viewType: string): void;
|
$unregisterEditorProvider(viewType: string): void;
|
||||||
|
|
||||||
@@ -628,6 +635,8 @@ export interface ExtHostWebviewsShape {
|
|||||||
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
|
$onSaveAs(resource: UriComponents, viewType: string, targetResource: UriComponents): Promise<void>;
|
||||||
|
|
||||||
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
$backup(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
|
||||||
|
|
||||||
|
$onMoveCustomEditor(handle: WebviewPanelHandle, newResource: UriComponents, viewType: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MainThreadUrlsShape extends IDisposable {
|
export interface MainThreadUrlsShape extends IDisposable {
|
||||||
@@ -1315,6 +1324,7 @@ export interface ExtHostTerminalServiceShape {
|
|||||||
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||||
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||||
|
$handleLink(id: number, link: string): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtHostSCMShape {
|
export interface ExtHostSCMShape {
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ export class ExtHostProgress implements ExtHostProgressShape {
|
|||||||
|
|
||||||
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
||||||
const handle = this._handles++;
|
const handle = this._handles++;
|
||||||
const { title, location, cancellable, viewId } = options;
|
const { title, location, cancellable } = options;
|
||||||
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||||
|
|
||||||
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location, viewId), title, source, cancellable }, extension);
|
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }, extension);
|
||||||
return this._withProgress(handle, task, !!cancellable);
|
return this._withProgress(handle, task, !!cancellable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { timeout } from 'vs/base/common/async';
|
|||||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||||
|
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||||
|
|
||||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
|||||||
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
|
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
|
||||||
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||||
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||||
|
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
|
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
|
||||||
@@ -295,6 +297,9 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||||||
protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
|
protected _extensionTerminalAwaitingStart: { [id: number]: { initialDimensions: ITerminalDimensionsDto | undefined } | undefined } = {};
|
||||||
protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
|
protected _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
|
||||||
|
|
||||||
|
private readonly _bufferer: TerminalDataBufferer;
|
||||||
|
private readonly _linkHandlers: Set<vscode.TerminalLinkHandler> = new Set();
|
||||||
|
|
||||||
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
|
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
|
||||||
public get terminals(): ExtHostTerminal[] { return this._terminals; }
|
public get terminals(): ExtHostTerminal[] { return this._terminals; }
|
||||||
|
|
||||||
@@ -309,8 +314,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||||||
protected readonly _onDidWriteTerminalData: Emitter<vscode.TerminalDataWriteEvent>;
|
protected readonly _onDidWriteTerminalData: Emitter<vscode.TerminalDataWriteEvent>;
|
||||||
public get onDidWriteTerminalData(): Event<vscode.TerminalDataWriteEvent> { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; }
|
public get onDidWriteTerminalData(): Event<vscode.TerminalDataWriteEvent> { return this._onDidWriteTerminalData && this._onDidWriteTerminalData.event; }
|
||||||
|
|
||||||
private readonly _bufferer: TerminalDataBufferer;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IExtHostRpcService extHostRpc: IExtHostRpcService
|
@IExtHostRpcService extHostRpc: IExtHostRpcService
|
||||||
) {
|
) {
|
||||||
@@ -535,6 +538,38 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable {
|
||||||
|
this._linkHandlers.add(handler);
|
||||||
|
if (this._linkHandlers.size === 1) {
|
||||||
|
this._proxy.$startHandlingLinks();
|
||||||
|
}
|
||||||
|
return new VSCodeDisposable(() => {
|
||||||
|
this._linkHandlers.delete(handler);
|
||||||
|
if (this._linkHandlers.size === 0) {
|
||||||
|
this._proxy.$stopHandlingLinks();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $handleLink(id: number, link: string): Promise<boolean> {
|
||||||
|
const terminal = this._getTerminalById(id);
|
||||||
|
if (!terminal) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call each handler synchronously so multiple handlers aren't triggered at once
|
||||||
|
const it = this._linkHandlers.values();
|
||||||
|
let next = it.next();
|
||||||
|
while (!next.done) {
|
||||||
|
const handled = await next.value.handleLink(terminal, link);
|
||||||
|
if (handled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
next = it.next();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private _onProcessExit(id: number, exitCode: number | undefined): void {
|
private _onProcessExit(id: number, exitCode: number | undefined): void {
|
||||||
this._bufferer.stopBuffering(id);
|
this._bufferer.stopBuffering(id);
|
||||||
|
|
||||||
|
|||||||
@@ -1093,12 +1093,15 @@ export namespace EndOfLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace ProgressLocation {
|
export namespace ProgressLocation {
|
||||||
export function from(loc: vscode.ProgressLocation, viewId?: string): MainProgressLocation | string {
|
export function from(loc: vscode.ProgressLocation | { viewId: string }): MainProgressLocation | string {
|
||||||
|
if (typeof loc === 'object') {
|
||||||
|
return loc.viewId;
|
||||||
|
}
|
||||||
|
|
||||||
switch (loc) {
|
switch (loc) {
|
||||||
case types.ProgressLocation.SourceControl: return MainProgressLocation.Scm;
|
case types.ProgressLocation.SourceControl: return MainProgressLocation.Scm;
|
||||||
case types.ProgressLocation.Window: return MainProgressLocation.Window;
|
case types.ProgressLocation.Window: return MainProgressLocation.Window;
|
||||||
case types.ProgressLocation.Notification: return MainProgressLocation.Notification;
|
case types.ProgressLocation.Notification: return MainProgressLocation.Notification;
|
||||||
case types.ProgressLocation.View: return viewId ?? '';
|
|
||||||
}
|
}
|
||||||
throw new Error(`Unknown 'ProgressLocation'`);
|
throw new Error(`Unknown 'ProgressLocation'`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2082,8 +2082,7 @@ export class Task implements vscode.Task2 {
|
|||||||
export enum ProgressLocation {
|
export enum ProgressLocation {
|
||||||
SourceControl = 1,
|
SourceControl = 1,
|
||||||
Window = 10,
|
Window = 10,
|
||||||
Notification = 15,
|
Notification = 15
|
||||||
View = 25
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@es5ClassCompat
|
@es5ClassCompat
|
||||||
|
|||||||
@@ -488,7 +488,9 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
|||||||
const disposables = new DisposableStore();
|
const disposables = new DisposableStore();
|
||||||
if ('resolveCustomTextEditor' in provider) {
|
if ('resolveCustomTextEditor' in provider) {
|
||||||
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
|
||||||
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options);
|
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options, {
|
||||||
|
supportsMove: !!provider.moveCustomTextEditor,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
|
||||||
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options);
|
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options);
|
||||||
@@ -663,6 +665,26 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
|||||||
document._disposeEdits(editIds);
|
document._disposeEdits(editIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async $onMoveCustomEditor(handle: string, newResourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||||
|
const entry = this._editorProviders.get(viewType);
|
||||||
|
if (!entry) {
|
||||||
|
throw new Error(`No provider found for '${viewType}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor) {
|
||||||
|
throw new Error(`Provider does not implement move '${viewType}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const webview = this.getWebviewPanel(handle);
|
||||||
|
if (!webview) {
|
||||||
|
throw new Error(`No webview found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resource = URI.revive(newResourceComponents);
|
||||||
|
const document = this._extHostDocuments.getDocument(resource);
|
||||||
|
await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview);
|
||||||
|
}
|
||||||
|
|
||||||
async $undo(resourceComponents: UriComponents, viewType: string, editId: number): Promise<void> {
|
async $undo(resourceComponents: UriComponents, viewType: string, editId: number): Promise<void> {
|
||||||
const document = this.getCustomDocument(viewType, resourceComponents);
|
const document = this.getCustomDocument(viewType, resourceComponents);
|
||||||
return document._undo(editId);
|
return document._undo(editId);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
|||||||
|
|
||||||
// Do this when extension service exists, but extensions are not being activated yet.
|
// Do this when extension service exists, but extensions are not being activated yet.
|
||||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||||
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy);
|
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._logService, this._mainThreadTelemetryProxy, this._initData);
|
||||||
|
|
||||||
// Use IPC messages to forward console-calls, note that the console is
|
// Use IPC messages to forward console-calls, note that the console is
|
||||||
// already patched to use`process.send()`
|
// already patched to use`process.send()`
|
||||||
|
|||||||
@@ -24,11 +24,23 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||||
|
|
||||||
export interface IResourceLabelProps {
|
export interface IResourceLabelProps {
|
||||||
resource?: URI;
|
resource?: URI | { master?: URI, detail?: URI };
|
||||||
name?: string | string[];
|
name?: string | string[];
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toResource(props: IResourceLabelProps | undefined): URI | undefined {
|
||||||
|
if (!props || !props.resource) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (URI.isUri(props.resource)) {
|
||||||
|
return props.resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return props.resource.master;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IResourceLabelOptions extends IIconLabelValueOptions {
|
export interface IResourceLabelOptions extends IIconLabelValueOptions {
|
||||||
fileKind?: FileKind;
|
fileKind?: FileKind;
|
||||||
fileDecorations?: { colors: boolean, badges: boolean };
|
fileDecorations?: { colors: boolean, badges: boolean };
|
||||||
@@ -289,11 +301,16 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyFileDecorationsChanges(e: IResourceDecorationChangeEvent): void {
|
notifyFileDecorationsChanges(e: IResourceDecorationChangeEvent): void {
|
||||||
if (!this.options || !this.label || !this.label.resource) {
|
if (!this.options) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.fileDecorations && e.affectsResource(this.label.resource)) {
|
const resource = toResource(this.label);
|
||||||
|
if (!resource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.fileDecorations && e.affectsResource(resource)) {
|
||||||
this.render(false);
|
this.render(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,13 +328,13 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyFormattersChange(scheme: string): void {
|
notifyFormattersChange(scheme: string): void {
|
||||||
if (this.label?.resource?.scheme === scheme) {
|
if (toResource(this.label)?.scheme === scheme) {
|
||||||
this.render(false);
|
this.render(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyUntitledLabelChange(resource: URI): void {
|
notifyUntitledLabelChange(resource: URI): void {
|
||||||
if (isEqual(resource, this.label?.resource)) {
|
if (isEqual(resource, toResource(this.label))) {
|
||||||
this.render(false);
|
this.render(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +364,10 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void {
|
setResource(label: IResourceLabelProps, options: IResourceLabelOptions = Object.create(null)): void {
|
||||||
/*if (label.resource?.scheme === Schemas.untitled) { {{SQL CARBON EDIT}} we don't want to special case untitled files
|
/*const resource = toResource(this.label); {{SQL CARBON EDIT}} we don't want to special case untitled files
|
||||||
|
const isMasterDetail = this.label?.resource && !URI.isUri(this.label.resource);
|
||||||
|
|
||||||
|
if (!isMasterDetail && resource?.scheme === Schemas.untitled) {
|
||||||
// Untitled labels are very dynamic because they may change
|
// Untitled labels are very dynamic because they may change
|
||||||
// whenever the content changes (unless a path is associated).
|
// whenever the content changes (unless a path is associated).
|
||||||
// As such we always ask the actual editor for it's name and
|
// As such we always ask the actual editor for it's name and
|
||||||
@@ -355,7 +375,11 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
// provided. If they are not provided from the label we got
|
// provided. If they are not provided from the label we got
|
||||||
// we assume that the client does not want to display them
|
// we assume that the client does not want to display them
|
||||||
// and as such do not override.
|
// and as such do not override.
|
||||||
const untitledModel = this.textFileService.untitled.get(label.resource);
|
//
|
||||||
|
// We do not touch the label if it represents a master-detail
|
||||||
|
// because in that case we expect it to carry a proper label
|
||||||
|
// and description.
|
||||||
|
const untitledModel = this.textFileService.untitled.get(resource);
|
||||||
if (untitledModel && !untitledModel.hasAssociatedFilePath) {
|
if (untitledModel && !untitledModel.hasAssociatedFilePath) {
|
||||||
if (typeof label.name === 'string') {
|
if (typeof label.name === 'string') {
|
||||||
label.name = untitledModel.name;
|
label.name = untitledModel.name;
|
||||||
@@ -415,7 +439,7 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private hasPathLabelChanged(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean {
|
private hasPathLabelChanged(newLabel: IResourceLabelProps, newOptions?: IResourceLabelOptions): boolean {
|
||||||
const newResource = newLabel ? newLabel.resource : undefined;
|
const newResource = toResource(newLabel);
|
||||||
|
|
||||||
return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource);
|
return !!newResource && this.computedPathLabel !== this.labelService.getUriLabel(newResource);
|
||||||
}
|
}
|
||||||
@@ -444,7 +468,8 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.label) {
|
if (this.label) {
|
||||||
const detectedModeId = this.label.resource ? withNullAsUndefined(detectModeId(this.modelService, this.modeService, this.label.resource)) : undefined;
|
const resource = toResource(this.label);
|
||||||
|
const detectedModeId = resource ? withNullAsUndefined(detectModeId(this.modelService, this.modeService, resource)) : undefined;
|
||||||
if (this.lastKnownDetectedModeId !== detectedModeId) {
|
if (this.lastKnownDetectedModeId !== detectedModeId) {
|
||||||
clearIconCache = true;
|
clearIconCache = true;
|
||||||
this.lastKnownDetectedModeId = detectedModeId;
|
this.lastKnownDetectedModeId = detectedModeId;
|
||||||
@@ -463,14 +488,15 @@ class ResourceLabelWidget extends IconLabel {
|
|||||||
|
|
||||||
const iconLabelOptions: IIconLabelValueOptions & { extraClasses: string[] } = {
|
const iconLabelOptions: IIconLabelValueOptions & { extraClasses: string[] } = {
|
||||||
title: '',
|
title: '',
|
||||||
italic: this.options && this.options.italic,
|
italic: this.options?.italic,
|
||||||
matches: this.options && this.options.matches,
|
strikethrough: this.options?.strikethrough,
|
||||||
|
matches: this.options?.matches,
|
||||||
extraClasses: [],
|
extraClasses: [],
|
||||||
separator: this.options?.separator,
|
separator: this.options?.separator,
|
||||||
domId: this.options?.domId
|
domId: this.options?.domId
|
||||||
};
|
};
|
||||||
|
|
||||||
const resource = this.label.resource;
|
const resource = toResource(this.label);
|
||||||
const label = this.label.name;
|
const label = this.label.name;
|
||||||
|
|
||||||
if (this.options && typeof this.options.title === 'string') {
|
if (this.options && typeof this.options.title === 'string') {
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
|||||||
// Instantiate composite from registry otherwise
|
// Instantiate composite from registry otherwise
|
||||||
const compositeDescriptor = this.registry.getComposite(id);
|
const compositeDescriptor = this.registry.getComposite(id);
|
||||||
if (compositeDescriptor) {
|
if (compositeDescriptor) {
|
||||||
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, assertIsDefined(this.progressBar), compositeDescriptor.id, !!isActive, undefined);
|
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, assertIsDefined(this.progressBar), compositeDescriptor.id, !!isActive);
|
||||||
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||||
[IEditorProgressService, compositeProgressIndicator] // provide the editor progress service for any editors instantiated within the composite
|
[IEditorProgressService, compositeProgressIndicator] // provide the editor progress service for any editors instantiated within the composite
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { IQuickPickSeparator, quickPickItemScorerAccessor, IQuickPickItemWithResource } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickPickSeparator, quickPickItemScorerAccessor, IQuickPickItemWithResource, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { PickerQuickAccessProvider, IPickerQuickAccessItem, TriggerAction } from 'vs/platform/quickinput/common/quickAccess';
|
import { PickerQuickAccessProvider, IPickerQuickAccessItem, TriggerAction } from 'vs/platform/quickinput/browser/pickerQuickAccess';
|
||||||
import { IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
import { IEditorGroupsService, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||||
import { EditorsOrder, IEditorIdentifier, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
import { EditorsOrder, IEditorIdentifier, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
@@ -28,6 +28,12 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
|
|||||||
super(prefix);
|
super(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected configure(picker: IQuickPick<IEditorQuickPickItem>): void {
|
||||||
|
|
||||||
|
// Allow to open editors in background without closing picker
|
||||||
|
picker.canAcceptInBackground = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected getPicks(filter: string): Array<IEditorQuickPickItem | IQuickPickSeparator> {
|
protected getPicks(filter: string): Array<IEditorQuickPickItem | IQuickPickSeparator> {
|
||||||
const query = prepareQuery(filter);
|
const query = prepareQuery(filter);
|
||||||
const scorerCache = Object.create(null);
|
const scorerCache = Object.create(null);
|
||||||
@@ -108,7 +114,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
|
|||||||
|
|
||||||
return TriggerAction.REFRESH_PICKER;
|
return TriggerAction.REFRESH_PICKER;
|
||||||
},
|
},
|
||||||
accept: () => this.editorGroupService.getGroup(groupId)?.openEditor(editor),
|
accept: (keyMods, event) => this.editorGroupService.getGroup(groupId)?.openEditor(editor, { preserveFocus: event.inBackground }),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,9 +260,6 @@ export class NoTabsTitleControl extends TitleControl {
|
|||||||
this.updateEditorDirty(editor);
|
this.updateEditorDirty(editor);
|
||||||
|
|
||||||
// Editor Label
|
// Editor Label
|
||||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
|
||||||
const name = editor.getName();
|
|
||||||
|
|
||||||
const { labelFormat } = this.accessor.partOptions;
|
const { labelFormat } = this.accessor.partOptions;
|
||||||
let description: string;
|
let description: string;
|
||||||
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
|
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
|
||||||
@@ -278,7 +275,19 @@ export class NoTabsTitleControl extends TitleControl {
|
|||||||
title = ''; // dont repeat what is already shown
|
title = ''; // dont repeat what is already shown
|
||||||
}
|
}
|
||||||
|
|
||||||
editorLabel.setResource({ name, description, resource }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
|
editorLabel.setResource(
|
||||||
|
{
|
||||||
|
resource: toResource(editor, { supportSideBySide: SideBySideEditor.BOTH }),
|
||||||
|
name: editor.getName(),
|
||||||
|
description
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
italic: !isEditorPinned,
|
||||||
|
extraClasses: ['no-tabs', 'title-label']
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (isGroupActive) {
|
if (isGroupActive) {
|
||||||
editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND) || '';
|
editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND) || '';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -975,11 +975,15 @@ export class TabsTitleControl extends TitleControl {
|
|||||||
tabContainer.title = title;
|
tabContainer.title = title;
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
tabLabelWidget.setResource(
|
||||||
tabLabelWidget.setResource({ name, description, resource }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
{ name, description, resource: toResource(editor, { supportSideBySide: SideBySideEditor.BOTH }) },
|
||||||
|
{ title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) }
|
||||||
|
);
|
||||||
|
|
||||||
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor)); // {{SQL CARBON EDIT}} -- Display the editor's tab color
|
this.setEditorTabColor(editor, tabContainer, this.group.isActive(editor)); // {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||||
|
|
||||||
// Tests helper
|
// Tests helper
|
||||||
|
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||||
if (resource) {
|
if (resource) {
|
||||||
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
|
tabContainer.setAttribute('data-resource-name', basenameOrAuthority(resource));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -151,7 +151,10 @@ class NotificationMessageRenderer {
|
|||||||
const anchor = $('a', { href: node.href, title: title, }, node.label);
|
const anchor = $('a', { href: node.href, title: title, }, node.label);
|
||||||
|
|
||||||
if (actionHandler) {
|
if (actionHandler) {
|
||||||
actionHandler.toDispose.add(addDisposableListener(anchor, EventType.CLICK, () => actionHandler.callback(node.href)));
|
actionHandler.toDispose.add(addDisposableListener(anchor, EventType.CLICK, e => {
|
||||||
|
EventHelper.stop(e, true);
|
||||||
|
actionHandler.callback(node.href);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
messageContainer.appendChild(anchor);
|
messageContainer.appendChild(anchor);
|
||||||
|
|||||||
@@ -171,6 +171,10 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon {
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon::before {
|
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon::before {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user