use config write API to write file associations (for #11974)
This commit is contained in:
parent
91115aa714
commit
8335651043
1 changed files with 52 additions and 24 deletions
|
@ -21,10 +21,10 @@ import {IMode} from 'vs/editor/common/modes';
|
||||||
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
|
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
|
||||||
import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor';
|
import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor';
|
||||||
import {IDisposable, combinedDisposable, dispose} from 'vs/base/common/lifecycle';
|
import {IDisposable, combinedDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||||
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
|
|
||||||
import {IMessageService, Severity} from 'vs/platform/message/common/message';
|
import {IMessageService, Severity} from 'vs/platform/message/common/message';
|
||||||
|
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||||
|
import {IConfigurationEditingService, ConfigurationTarget} from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||||
import {IEditorAction, ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon';
|
import {IEditorAction, ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon';
|
||||||
import {OpenGlobalSettingsAction} from 'vs/workbench/browser/actions/openSettings';
|
|
||||||
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';
|
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';
|
||||||
import {TrimTrailingWhitespaceAction} from 'vs/editor/contrib/linesOperations/common/linesOperations';
|
import {TrimTrailingWhitespaceAction} from 'vs/editor/contrib/linesOperations/common/linesOperations';
|
||||||
import {EndOfLineSequence, ITokenizedModel, EditorType, ITextModel, IDiffEditorModel, IEditor} from 'vs/editor/common/editorCommon';
|
import {EndOfLineSequence, ITokenizedModel, EditorType, ITextModel, IDiffEditorModel, IEditor} from 'vs/editor/common/editorCommon';
|
||||||
|
@ -299,7 +299,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||||
this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()),
|
this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()),
|
||||||
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
|
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
|
||||||
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
|
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
|
||||||
TabFocus.onDidChangeTabFocus((e) => this.onTabFocusModeChange())
|
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange())
|
||||||
);
|
);
|
||||||
|
|
||||||
return combinedDisposable(this.toDispose);
|
return combinedDisposable(this.toDispose);
|
||||||
|
@ -533,7 +533,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||||
info.charactersSelected = 0;
|
info.charactersSelected = 0;
|
||||||
const textModel = getTextModel(editorWidget);
|
const textModel = getTextModel(editorWidget);
|
||||||
if (textModel) {
|
if (textModel) {
|
||||||
info.selections.forEach((selection) => {
|
info.selections.forEach(selection => {
|
||||||
info.charactersSelected += textModel.getValueLengthInRange(selection);
|
info.charactersSelected += textModel.getValueLengthInRange(selection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -633,7 +633,9 @@ export class ChangeModeAction extends Action {
|
||||||
actionLabel: string,
|
actionLabel: string,
|
||||||
@IModeService private modeService: IModeService,
|
@IModeService private modeService: IModeService,
|
||||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||||
|
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
|
||||||
@IMessageService private messageService: IMessageService,
|
@IMessageService private messageService: IMessageService,
|
||||||
|
@IConfigurationService private configurationService: IConfigurationService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService,
|
@IInstantiationService private instantiationService: IInstantiationService,
|
||||||
@IQuickOpenService private quickOpenService: IQuickOpenService
|
@IQuickOpenService private quickOpenService: IQuickOpenService
|
||||||
) {
|
) {
|
||||||
|
@ -641,7 +643,6 @@ export class ChangeModeAction extends Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
public run(): TPromise<any> {
|
public run(): TPromise<any> {
|
||||||
const languages = this.modeService.getRegisteredLanguageNames();
|
|
||||||
let activeEditor = this.editorService.getActiveEditor();
|
let activeEditor = this.editorService.getActiveEditor();
|
||||||
if (!(activeEditor instanceof BaseTextEditor)) {
|
if (!(activeEditor instanceof BaseTextEditor)) {
|
||||||
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||||
|
@ -661,6 +662,7 @@ export class ChangeModeAction extends Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
// All languages are valid picks
|
// All languages are valid picks
|
||||||
|
const languages = this.modeService.getRegisteredLanguageNames();
|
||||||
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
|
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
|
||||||
let description: string;
|
let description: string;
|
||||||
if (currentModeId === lang) {
|
if (currentModeId === lang) {
|
||||||
|
@ -677,19 +679,15 @@ export class ChangeModeAction extends Action {
|
||||||
picks[0].separator = { border: true, label: nls.localize('languagesPicks', "languages") };
|
picks[0].separator = { border: true, label: nls.localize('languagesPicks', "languages") };
|
||||||
|
|
||||||
// Offer action to configure via settings
|
// Offer action to configure via settings
|
||||||
let configureLabel = nls.localize('configureAssociations', "Configure File Associations...");
|
let configureModeAssociations: IPickOpenEntry;
|
||||||
if (fileinput) {
|
if (fileinput) {
|
||||||
const resource = fileinput.getResource();
|
const resource = fileinput.getResource();
|
||||||
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
|
configureModeAssociations = {
|
||||||
if (ext) {
|
label: nls.localize('configureAssociationsExt', "Configure File Association for '{0}'...", paths.extname(resource.fsPath) || paths.basename(resource.fsPath))
|
||||||
configureLabel = nls.localize('configureAssociationsExt', "Configure File Association for '{0}'...", ext);
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const configureModeAssociations: IPickOpenEntry = {
|
picks.unshift(configureModeAssociations);
|
||||||
label: configureLabel
|
}
|
||||||
};
|
|
||||||
picks.unshift(configureModeAssociations);
|
|
||||||
|
|
||||||
// Offer to "Auto Detect"
|
// Offer to "Auto Detect"
|
||||||
const autoDetectMode: IPickOpenEntry = {
|
const autoDetectMode: IPickOpenEntry = {
|
||||||
|
@ -699,7 +697,7 @@ export class ChangeModeAction extends Action {
|
||||||
picks.unshift(autoDetectMode);
|
picks.unshift(autoDetectMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode") }).then((language) => {
|
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode") }).then(language => {
|
||||||
if (language) {
|
if (language) {
|
||||||
activeEditor = this.editorService.getActiveEditor();
|
activeEditor = this.editorService.getActiveEditor();
|
||||||
if (activeEditor instanceof BaseTextEditor) {
|
if (activeEditor instanceof BaseTextEditor) {
|
||||||
|
@ -720,16 +718,13 @@ export class ChangeModeAction extends Action {
|
||||||
if (language === autoDetectMode) {
|
if (language === autoDetectMode) {
|
||||||
mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(getUntitledOrFileResource(activeEditor.input, true).fsPath, textModel.getLineContent(1));
|
mode = this.modeService.getOrCreateModeByFilenameOrFirstLine(getUntitledOrFileResource(activeEditor.input, true).fsPath, textModel.getLineContent(1));
|
||||||
} else if (language === configureModeAssociations) {
|
} else if (language === configureModeAssociations) {
|
||||||
const action = this.instantiationService.createInstance(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL);
|
this.configureFileAssociation(fileinput.getResource());
|
||||||
action.run().done(() => action.dispose(), errors.onUnexpectedError);
|
|
||||||
|
|
||||||
this.messageService.show(Severity.Info, nls.localize('persistFileAssociations', "You can configure filename to language associations in the **files.associations** section. The changes may need a restart to take effect on already opened files."));
|
|
||||||
} else {
|
} else {
|
||||||
mode = this.modeService.getOrCreateModeByLanguageName(language.label);
|
mode = this.modeService.getOrCreateModeByLanguageName(language.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change mode
|
// Change mode
|
||||||
models.forEach((textModel) => {
|
models.forEach(textModel => {
|
||||||
if (!!(<ITokenizedModel>textModel).getMode) {
|
if (!!(<ITokenizedModel>textModel).getMode) {
|
||||||
(<ITokenizedModel>textModel).setMode(mode);
|
(<ITokenizedModel>textModel).setMode(mode);
|
||||||
}
|
}
|
||||||
|
@ -738,6 +733,39 @@ export class ChangeModeAction extends Action {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private configureFileAssociation(resource: uri): void {
|
||||||
|
const languages = this.modeService.getRegisteredLanguageNames();
|
||||||
|
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
|
||||||
|
return <IPickOpenEntry>{
|
||||||
|
id: this.modeService.getModeIdForLanguageName(lang.toLowerCase()),
|
||||||
|
label: lang
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).done(() => {
|
||||||
|
const extension = paths.extname(resource.fsPath);
|
||||||
|
const basename = paths.basename(resource.fsPath);
|
||||||
|
|
||||||
|
this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => {
|
||||||
|
if (language) {
|
||||||
|
const config = this.configurationService.getConfiguration<IFilesConfiguration>();
|
||||||
|
const currentAssociations = (config && config.files && config.files.associations) || Object.create(null);
|
||||||
|
|
||||||
|
let associationKey: string;
|
||||||
|
if (extension && basename[0] !== '.') {
|
||||||
|
associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of <name>.<ext>
|
||||||
|
} else {
|
||||||
|
associationKey = basename; // otherwise use the basename (e.g. .gitignore, Dockerfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssociations[associationKey] = language.id;
|
||||||
|
|
||||||
|
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'files.associations', value: currentAssociations }).done(null, (error) => this.messageService.show(Severity.Error, error.toString()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChangeEOLEntry extends IPickOpenEntry {
|
export interface IChangeEOLEntry extends IPickOpenEntry {
|
||||||
|
@ -828,7 +856,7 @@ export class ChangeEOLAction extends Action {
|
||||||
|
|
||||||
const selectedIndex = (textModel.getEOL() === '\n') ? 0 : 1;
|
const selectedIndex = (textModel.getEOL() === '\n') ? 0 : 1;
|
||||||
|
|
||||||
return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then((eol) => {
|
return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => {
|
||||||
if (eol) {
|
if (eol) {
|
||||||
activeEditor = this.editorService.getActiveEditor();
|
activeEditor = this.editorService.getActiveEditor();
|
||||||
if (activeEditor instanceof BaseTextEditor && isWritableCodeEditor(activeEditor)) {
|
if (activeEditor instanceof BaseTextEditor && isWritableCodeEditor(activeEditor)) {
|
||||||
|
@ -887,7 +915,7 @@ export class ChangeEncodingAction extends Action {
|
||||||
pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return pickActionPromise.then((action) => {
|
return pickActionPromise.then(action => {
|
||||||
if (!action) {
|
if (!action) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -927,7 +955,7 @@ export class ChangeEncodingAction extends Action {
|
||||||
return this.quickOpenService.pick(picks, {
|
return this.quickOpenService.pick(picks, {
|
||||||
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
|
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
|
||||||
autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 }
|
autoFocus: { autoFocusIndex: typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : void 0 }
|
||||||
}).then((encoding) => {
|
}).then(encoding => {
|
||||||
if (encoding) {
|
if (encoding) {
|
||||||
activeEditor = this.editorService.getActiveEditor();
|
activeEditor = this.editorService.getActiveEditor();
|
||||||
encodingSupport = <any>asFileOrUntitledEditorInput(activeEditor.input);
|
encodingSupport = <any>asFileOrUntitledEditorInput(activeEditor.input);
|
||||||
|
|
Loading…
Reference in a new issue