|
|
|
|
@@ -40,7 +40,7 @@ import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/
|
|
|
|
|
import { URI } from 'vs/base/common/uri';
|
|
|
|
|
import { FileKind } from 'vs/platform/files/common/files';
|
|
|
|
|
import { compareFileNames } from 'vs/base/common/comparers';
|
|
|
|
|
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
|
|
|
|
import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters';
|
|
|
|
|
import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views';
|
|
|
|
|
import { localize } from 'vs/nls';
|
|
|
|
|
import { flatten, find } from 'vs/base/common/arrays';
|
|
|
|
|
@@ -77,6 +77,39 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
|
|
|
|
|
|
|
|
|
type TreeElement = ISCMResourceGroup | IResourceNode<ISCMResource, ISCMResourceGroup> | ISCMResource;
|
|
|
|
|
|
|
|
|
|
function splitMatches(uri: URI, filterData: FuzzyScore | undefined): [IMatch[] | undefined, IMatch[] | undefined] {
|
|
|
|
|
let matches: IMatch[] | undefined;
|
|
|
|
|
let descriptionMatches: IMatch[] | undefined;
|
|
|
|
|
|
|
|
|
|
if (filterData) {
|
|
|
|
|
matches = [];
|
|
|
|
|
descriptionMatches = [];
|
|
|
|
|
|
|
|
|
|
const fileName = basename(uri);
|
|
|
|
|
const allMatches = createMatches(filterData);
|
|
|
|
|
|
|
|
|
|
for (const match of allMatches) {
|
|
|
|
|
if (match.start < fileName.length) {
|
|
|
|
|
matches!.push(
|
|
|
|
|
{
|
|
|
|
|
start: match.start,
|
|
|
|
|
end: Math.min(match.end, fileName.length)
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
descriptionMatches!.push(
|
|
|
|
|
{
|
|
|
|
|
start: match.start - (fileName.length + 1),
|
|
|
|
|
end: match.end - (fileName.length + 1)
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [matches, descriptionMatches];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ResourceGroupTemplate {
|
|
|
|
|
readonly name: HTMLElement;
|
|
|
|
|
readonly count: CountBadge;
|
|
|
|
|
@@ -188,7 +221,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IReso
|
|
|
|
|
renderTemplate(container: HTMLElement): ResourceTemplate {
|
|
|
|
|
const element = append(container, $('.resource'));
|
|
|
|
|
const name = append(element, $('.name'));
|
|
|
|
|
const fileLabel = this.labels.create(name, { supportHighlights: true });
|
|
|
|
|
const fileLabel = this.labels.create(name, { supportDescriptionHighlights: true, supportHighlights: true });
|
|
|
|
|
const actionsContainer = append(fileLabel.element, $('.actions'));
|
|
|
|
|
const actionBar = new ActionBar(actionsContainer, {
|
|
|
|
|
actionViewItemProvider: this.actionViewItemProvider,
|
|
|
|
|
@@ -214,11 +247,13 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IReso
|
|
|
|
|
const fileKind = ResourceTree.isResourceNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE;
|
|
|
|
|
const viewModel = this.viewModelProvider();
|
|
|
|
|
|
|
|
|
|
const [matches, descriptionMatches] = splitMatches(uri, node.filterData);
|
|
|
|
|
template.fileLabel.setFile(uri, {
|
|
|
|
|
fileDecorations: { colors: false, badges: !icon },
|
|
|
|
|
hidePath: viewModel.mode === ViewModelMode.Tree,
|
|
|
|
|
fileKind,
|
|
|
|
|
matches: createMatches(node.filterData)
|
|
|
|
|
matches,
|
|
|
|
|
descriptionMatches
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
template.actionBar.clear();
|
|
|
|
|
@@ -270,10 +305,12 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IReso
|
|
|
|
|
const label = compressed.elements.map(e => e.name).join('/');
|
|
|
|
|
const fileKind = FileKind.FOLDER;
|
|
|
|
|
|
|
|
|
|
const [matches, descriptionMatches] = splitMatches(folder.uri, node.filterData);
|
|
|
|
|
template.fileLabel.setResource({ resource: folder.uri, name: label }, {
|
|
|
|
|
fileDecorations: { colors: false, badges: true },
|
|
|
|
|
fileKind,
|
|
|
|
|
matches: createMatches(node.filterData)
|
|
|
|
|
matches,
|
|
|
|
|
descriptionMatches
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
template.actionBar.clear();
|
|
|
|
|
@@ -358,13 +395,20 @@ export class SCMTreeSorter implements ITreeSorter<TreeElement> {
|
|
|
|
|
|
|
|
|
|
export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyboardNavigationLabelProvider<TreeElement> {
|
|
|
|
|
|
|
|
|
|
constructor(@ILabelService private readonly labelService: ILabelService) { }
|
|
|
|
|
|
|
|
|
|
getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } | undefined {
|
|
|
|
|
if (ResourceTree.isResourceNode(element)) {
|
|
|
|
|
return element.name;
|
|
|
|
|
} else if (isSCMResourceGroup(element)) {
|
|
|
|
|
return element.label;
|
|
|
|
|
} else {
|
|
|
|
|
return basename(element.sourceUri);
|
|
|
|
|
// Since a match in the file name takes precedence over a match
|
|
|
|
|
// in the folder name we are returning the label as file/folder.
|
|
|
|
|
const fileName = basename(element.sourceUri);
|
|
|
|
|
const filePath = this.labelService.getUriLabel(dirname(element.sourceUri), { relative: true });
|
|
|
|
|
|
|
|
|
|
return filePath.length !== 0 ? `${fileName} ${filePath}` : fileName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -638,6 +682,7 @@ export class ToggleViewModeAction extends Action {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class RepositoryPane extends ViewPane {
|
|
|
|
|
private readonly defaultInputFontFamily = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif';
|
|
|
|
|
|
|
|
|
|
private cachedHeight: number | undefined = undefined;
|
|
|
|
|
private cachedWidth: number | undefined = undefined;
|
|
|
|
|
@@ -766,13 +811,12 @@ export class RepositoryPane extends ViewPane {
|
|
|
|
|
cursorWidth: 1,
|
|
|
|
|
fontSize: 13,
|
|
|
|
|
lineHeight: 20,
|
|
|
|
|
fontFamily: ' -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif',
|
|
|
|
|
fontFamily: this.getInputEditorFontFamily(),
|
|
|
|
|
wrappingStrategy: 'advanced',
|
|
|
|
|
wrappingIndent: 'none',
|
|
|
|
|
padding: { top: 3, bottom: 3 },
|
|
|
|
|
quickSuggestions: false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
|
|
|
|
|
isSimpleWidget: true,
|
|
|
|
|
contributions: EditorExtensionsRegistry.getSomeEditorContributions([
|
|
|
|
|
@@ -796,6 +840,9 @@ export class RepositoryPane extends ViewPane {
|
|
|
|
|
this._register(this.inputEditor.onDidFocusEditorText(() => addClass(editorContainer, 'synthetic-focus')));
|
|
|
|
|
this._register(this.inputEditor.onDidBlurEditorText(() => removeClass(editorContainer, 'synthetic-focus')));
|
|
|
|
|
|
|
|
|
|
const onInputFontFamilyChanged = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.inputFontFamily'));
|
|
|
|
|
this._register(onInputFontFamilyChanged(() => this.inputEditor.updateOptions({ fontFamily: this.getInputEditorFontFamily() })));
|
|
|
|
|
|
|
|
|
|
let query: string | undefined;
|
|
|
|
|
|
|
|
|
|
if (this.repository.provider.rootUri) {
|
|
|
|
|
@@ -874,7 +921,7 @@ export class RepositoryPane extends ViewPane {
|
|
|
|
|
|
|
|
|
|
const filter = new SCMTreeFilter();
|
|
|
|
|
const sorter = new SCMTreeSorter(() => this.viewModel);
|
|
|
|
|
const keyboardNavigationLabelProvider = new SCMTreeKeyboardNavigationLabelProvider();
|
|
|
|
|
const keyboardNavigationLabelProvider = this.instantiationService.createInstance(SCMTreeKeyboardNavigationLabelProvider);
|
|
|
|
|
const identityProvider = new SCMResourceIdentityProvider();
|
|
|
|
|
|
|
|
|
|
this.tree = this.instantiationService.createInstance(
|
|
|
|
|
@@ -1114,6 +1161,20 @@ export class RepositoryPane extends ViewPane {
|
|
|
|
|
this.layoutBody(this.cachedHeight);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getInputEditorFontFamily(): string {
|
|
|
|
|
const inputFontFamily = this.configurationService.getValue<string>('scm.inputFontFamily').trim();
|
|
|
|
|
|
|
|
|
|
if (inputFontFamily.toLowerCase() === 'editor') {
|
|
|
|
|
return this.configurationService.getValue<string>('editor.fontFamily').trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inputFontFamily.length !== 0 && inputFontFamily.toLowerCase() !== 'default') {
|
|
|
|
|
return inputFontFamily;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.defaultInputFontFamily;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class RepositoryViewDescriptor implements IViewDescriptor {
|
|
|
|
|
|