Skip to content

Commit d406fc4

Browse files
committed
add workspace folders to model and picker microsoft#54370
1 parent cf27882 commit d406fc4

4 files changed

Lines changed: 111 additions & 40 deletions

File tree

src/vs/platform/workspace/common/workspace.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ export interface IWorkspaceContextService {
7777
isInsideWorkspace(resource: URI): boolean;
7878
}
7979

80+
export namespace IWorkspace {
81+
export function isIWorkspace(thing: any): thing is IWorkspace {
82+
return thing && typeof thing === 'object'
83+
&& typeof (thing as IWorkspace).id === 'string'
84+
&& typeof (thing as IWorkspace).name === 'string'
85+
&& Array.isArray((thing as IWorkspace).folders);
86+
}
87+
}
88+
8089
export interface IWorkspace {
8190

8291
/**
@@ -118,6 +127,15 @@ export interface IWorkspaceFolderData {
118127
readonly index: number;
119128
}
120129

130+
export namespace IWorkspaceFolder {
131+
export function isIWorkspaceFolder(thing: any): thing is IWorkspaceFolder {
132+
return thing && typeof thing === 'object'
133+
&& URI.isUri((thing as IWorkspaceFolder).uri)
134+
&& typeof (thing as IWorkspaceFolder).name === 'string'
135+
&& typeof (thing as IWorkspaceFolder).toResource === 'function';
136+
}
137+
}
138+
121139
export interface IWorkspaceFolder extends IWorkspaceFolderData {
122140

123141
/**

src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class Item extends BreadcrumbsItem {
7575
// file/folder
7676
let label = this._instantiationService.createInstance(FileLabel, container, {});
7777
label.setFile(this.element.uri, {
78+
extraClasses: [FileKind[this.element.kind].toLowerCase()],
7879
hidePath: true,
7980
hideIcon: this.element.kind !== FileKind.FILE || !this.options.showFileIcons,
8081
fileKind: this.element.kind,

src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class FileElement {
3434

3535
export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement;
3636

37-
type FileInfo = { path: FileElement[], folder: IWorkspaceFolder, showFolder: boolean };
37+
type FileInfo = { path: FileElement[], folder: IWorkspaceFolder };
3838

3939
export class EditorBreadcrumbsModel {
4040

@@ -102,14 +102,12 @@ export class EditorBreadcrumbsModel {
102102

103103
if (uri.scheme === Schemas.untitled) {
104104
return {
105-
showFolder: false,
106105
folder: undefined,
107106
path: []
108107
};
109108
}
110109

111110
let info: FileInfo = {
112-
showFolder: workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE,
113111
folder: workspaceService.getWorkspaceFolder(uri),
114112
path: []
115113
};
@@ -121,6 +119,10 @@ export class EditorBreadcrumbsModel {
121119
info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
122120
uri = uri.with({ path: paths.dirname(uri.path) });
123121
}
122+
123+
if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
124+
info.path.unshift(new FileElement(info.folder.uri, FileKind.ROOT_FOLDER));
125+
}
124126
return info;
125127
}
126128

src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts

Lines changed: 87 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/edito
2626
import { onUnexpectedError } from 'vs/base/common/errors';
2727
import { breadcrumbsPickerBackground } from 'vs/platform/theme/common/colorRegistry';
2828
import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters';
29+
import { IWorkspaceContextService, IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
2930

3031
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
3132
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
@@ -136,39 +137,61 @@ export abstract class BreadcrumbsPicker {
136137

137138
export class FileDataSource implements IDataSource {
138139

139-
private readonly _parents = new WeakMap<IFileStat, IFileStat>();
140+
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
140141

141142
constructor(
142143
@IFileService private readonly _fileService: IFileService,
143144
) { }
144145

145-
getId(tree: ITree, element: IFileStat | URI): string {
146-
return URI.isUri(element) ? element.toString() : element.resource.toString();
146+
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
147+
if (URI.isUri(element)) {
148+
return element.toString();
149+
} else if (IWorkspace.isIWorkspace(element)) {
150+
return element.id;
151+
} else if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
152+
return element.uri.toString();
153+
} else {
154+
return element.resource.toString();
155+
}
147156
}
148157

149-
hasChildren(tree: ITree, element: IFileStat | URI): boolean {
150-
return URI.isUri(element) || element.isDirectory;
158+
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
159+
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
151160
}
152161

153-
getChildren(tree: ITree, element: IFileStat | URI): TPromise<IFileStat[]> {
154-
return this._fileService.resolveFile(
155-
URI.isUri(element) ? element : element.resource
156-
).then(stat => {
157-
for (const child of stat.children) {
158-
this._parents.set(child, stat);
162+
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): TPromise<IWorkspaceFolder[] | IFileStat[]> {
163+
if (IWorkspace.isIWorkspace(element)) {
164+
return TPromise.as(element.folders).then(folders => {
165+
for (let child of folders) {
166+
this._parents.set(element, child);
167+
}
168+
return folders;
169+
});
170+
}
171+
let uri: URI;
172+
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
173+
uri = element.uri;
174+
} else if (URI.isUri(element)) {
175+
uri = element;
176+
} else {
177+
uri = element.resource;
178+
}
179+
return this._fileService.resolveFile(uri).then(stat => {
180+
for (let child of stat.children) {
181+
this._parents.set(stat, child);
159182
}
160183
return stat.children;
161184
});
162185
}
163186

164-
getParent(tree: ITree, element: IFileStat | URI): TPromise<IFileStat> {
165-
return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element));
187+
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): TPromise<IWorkspaceFolder | IFileStat> {
188+
return TPromise.as(this._parents.get(element));
166189
}
167190
}
168191

169192
export class FileRenderer implements IRenderer, IHighlightingRenderer {
170193

171-
private readonly _scores = new Map<string, FuzzyScore>();
194+
private readonly _scores = new Map<object, FuzzyScore>();
172195

173196
constructor(
174197
@IInstantiationService private readonly _instantiationService: IInstantiationService
@@ -186,13 +209,22 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer {
186209
return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true });
187210
}
188211

189-
renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void {
190-
templateData.setFile(element.resource, {
191-
hidePath: true,
192-
fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE,
193-
fileDecorations: { colors: true, badges: true },
194-
matches: createMatches((this._scores.get(element.resource.toString()) || [, []])[1])
195-
});
212+
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void {
213+
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
214+
templateData.setFile(element.uri, {
215+
hidePath: true,
216+
fileKind: FileKind.ROOT_FOLDER,
217+
fileDecorations: { colors: true, badges: true },
218+
matches: createMatches((this._scores.get(element) || [, []])[1])
219+
});
220+
} else {
221+
templateData.setFile(element.resource, {
222+
hidePath: true,
223+
fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE,
224+
fileDecorations: { colors: true, badges: true },
225+
matches: createMatches((this._scores.get(element) || [, []])[1])
226+
});
227+
}
196228
}
197229

198230
disposeTemplate(tree: ITree, templateId: string, templateData: FileLabel): void {
@@ -204,9 +236,9 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer {
204236
let topScore: FuzzyScore;
205237
let topElement: any;
206238
while (nav.next()) {
207-
let element = nav.current() as IFileStat;
239+
let element = nav.current() as IFileStat | IWorkspaceFolder;
208240
let score = fuzzyScore(pattern, element.name, undefined, true);
209-
this._scores.set(element.resource.toString(), score);
241+
this._scores.set(element, score);
210242
if (!topScore || score && topScore[0] < score[0]) {
211243
topScore = score;
212244
topElement = element;
@@ -217,31 +249,50 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer {
217249
}
218250

219251
export class FileSorter implements ISorter {
220-
compare(tree: ITree, a: IFileStat, b: IFileStat): number {
221-
if (a.isDirectory === b.isDirectory) {
222-
// same type -> compare on names
223-
return compareFileNames(a.name, b.name);
224-
} else if (a.isDirectory) {
225-
return -1;
252+
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
253+
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
254+
return a.index - b.index;
226255
} else {
227-
return 1;
256+
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
257+
// same type -> compare on names
258+
return compareFileNames(a.name, b.name);
259+
} else if ((a as IFileStat).isDirectory) {
260+
return -1;
261+
} else {
262+
return 1;
263+
}
228264
}
229265
}
230266
}
231267

232268
export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
233269

270+
constructor(
271+
parent: HTMLElement,
272+
@IInstantiationService instantiationService: IInstantiationService,
273+
@IThemeService themeService: IThemeService,
274+
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
275+
) {
276+
super(parent, instantiationService, themeService);
277+
}
278+
234279
protected _getInput(input: BreadcrumbElement): any {
235-
let { uri } = (input as FileElement);
236-
return dirname(uri);
280+
let { uri, kind } = (input as FileElement);
281+
if (kind === FileKind.ROOT_FOLDER) {
282+
return this._workspaceService.getWorkspace();
283+
} else {
284+
return dirname(uri);
285+
}
237286
}
238287

239288
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
240289
let { uri } = (input as FileElement);
241290
let nav = tree.getNavigator();
242291
while (nav.next()) {
243-
if (isEqual(uri, (nav.current() as IFileStat).resource)) {
244-
return nav.current();
292+
let cur = nav.current();
293+
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
294+
if (isEqual(uri, candidate)) {
295+
return cur;
245296
}
246297
}
247298
return undefined;
@@ -257,9 +308,8 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
257308

258309
protected _onDidChangeSelection(e: ISelectionEvent): void {
259310
let [first] = e.selection;
260-
let stat = first as IFileStat;
261-
if (stat && !stat.isDirectory) {
262-
this._onDidPickElement.fire(new FileElement(stat.resource, FileKind.FILE));
311+
if (first && !IWorkspaceFolder.isIWorkspaceFolder(first) && !(first as IFileStat).isDirectory) {
312+
this._onDidPickElement.fire(new FileElement((first as IFileStat).resource, FileKind.FILE));
263313
}
264314
}
265315
}

0 commit comments

Comments
 (0)