diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index a59f40dd2f4..be59e276ec8 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -73,6 +73,11 @@ import { RunAutomaticTasks } from 'vs/workbench/contrib/tasks/browser/runAutomat import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { format } from 'vs/base/common/jsonFormatter'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { applyEdits } from 'vs/base/common/jsonEdit'; +import { ITextEditor } from 'vs/workbench/common/editor'; +import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; @@ -220,7 +225,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService, - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @ITextModelService private readonly textModelResolverService: ITextModelService ) { super(); @@ -769,6 +775,60 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return false; } + private openEditorAtTask(resource: URI | undefined, task: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | string | undefined): Promise { + if (resource === undefined) { + return Promise.resolve(undefined); + } + let selection: ITextEditorSelection | undefined; + return this.fileService.readFile(resource).then(content => content.value).then(async content => { + if (!content) { + return undefined; + } + if (task) { + const contentValue = content.toString(); + let stringValue: string; + if (typeof task === 'string') { + stringValue = task; + } else { + const model = (await this.textModelResolverService.createModelReference(resource)).object.textEditorModel; + const { tabSize, insertSpaces } = model.getOptions(); + const eol = model.getEOL(); + const edits = format(JSON.stringify(task), undefined, { eol, tabSize, insertSpaces }); + let stringified = applyEdits(JSON.stringify(task), edits); + const regex = new RegExp(eol + '\\t', 'g'); + stringified = stringified.replace(regex, eol + '\t\t\t'); + const twoTabs = '\t\t'; + stringValue = twoTabs + stringified.slice(0, stringified.length - 1) + twoTabs + stringified.slice(stringified.length - 1); + } + + const index = contentValue.indexOf(stringValue); + let startLineNumber = 1; + for (let i = 0; i < index; i++) { + if (contentValue.charAt(i) === '\n') { + startLineNumber++; + } + } + let endLineNumber = startLineNumber; + for (let i = 0; i < stringValue.length; i++) { + if (stringValue.charAt(i) === '\n') { + endLineNumber++; + } + } + selection = startLineNumber > 1 ? { startLineNumber, startColumn: startLineNumber === endLineNumber ? 4 : 3, endLineNumber, endColumn: startLineNumber === endLineNumber ? undefined : 4 } : undefined; + } + + return this.editorService.openEditor({ + resource, + options: { + pinned: false, + forceReload: true, // because content might have changed + selection, + revealInCenterIfOutsideViewport: !!selection + } + }); + }); + } + public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise { const workspaceFolder = task.getWorkspaceFolder(); if (!workspaceFolder) { @@ -867,14 +927,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer */ this.telemetryService.publicLog(AbstractTaskService.CustomizationTelemetryEventName, event); if (openConfig) { - let resource = workspaceFolder.toResource('.vscode/tasks.json'); - this.editorService.openEditor({ - resource, - options: { - pinned: false, - forceReload: true // because content might have changed - } - }); + this.openEditorAtTask(workspaceFolder.toResource('.vscode/tasks.json'), toCustomize); } }); } @@ -896,12 +949,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } else { resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined; } - return this.editorService.openEditor({ - resource, - options: { - pinned: false - } - }).then(() => undefined); + return this.openEditorAtTask(resource, task ? task._label : undefined).then(() => undefined); } private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined {