Link

작업 제공자

사용자는 보통 작업을 Visual Studio Code의 tasks.json 파일에 정의합니다. 한편, 어떤 작업들은 소프트웨어 개발 도중 자동적으로 VS Code 익스텐션 작업 제공자에 의해 인식됩니다. VS Code에서 Tasks: Run Task 커맨드가 실행될때, 모든 활성 작업 제공자는 사용자가 실행 가능한 작업을 지시합니다. tasks.json파일은 사용자가 특정 폴더나 작업공간에 수동으로 작업을 정의해야하지만, 작업 제공자는 작업공간에 대하여 세부정보를 감지하고 해당하는 VS Code 작업을 자동으로 생성합니다. 예를 들어, 작업 제공자는 makeRakefile과 같은 특정 빌드 파일이 존재 하는지를 확인하여 빌드 작업을 생성 합니다. 이번 주제에서는 익스텐션이 어떻게 작업을 자동으로 인식하고 사용자에게 제공하는지 설명합니다.

이 가이드는 여러분에게 [Rakefiles]에 정의되어 있는 작업을 자동으로 감지하는 작업 제공자를 만드는 방법을 가르칩니다. 완전한 소스코드는 이곳에 있습니다: https://github.com/Microsoft/vscode-extension-samples/tree/master/task-provider-sample

작업 정의

시스템에서 작업을 특별하게 식별하기 위하여, 익스텐션의 작업 지시는 작업을 식별하는 속성을 지정해야만 합니다. Rake 예시에서 작업 정의는 다음과 같습니다 :

"taskDefinitions": [
    {
        "type": "rake",
        "required": [
            "task"
        ],
        "properties": {
            "task": {
                "type": "string",
                "description": "The Rake task to customize"
            },
            "file": {
                "type": "string",
                "description": "The Rake file that provides the task. Can be omitted."
            }
        }
    }
]

이는 rake 작업에 대하여 작업 정의를 지시합니다. 작업 정의는 taskfile의 2가지 속성을 가집니다. task는 Rake 작업의 이름이며 file은 작업을 포함하는 Rakefile의 위치를 가르킵니다. task는 필수 속성이며 file 속성은 옵션입니다. 만약 file속성이 생략 되었을 경우, 작업공간 폴더의 루트에 있는 Rakefile이 대신 사용 될 것입니다.

작업 제공자

코드 완성을 지원하는 언어 제공 익스텐션과 유사하게, 익스텐션은 가능한 모든 작업을 계산할 작업 제공자를 등록할 수 있습니다. 이는 다음 코드 snippet처럼 vscode.task 네임스페이스를 이용하여 가능합니다:

import * as vscode from 'vscode';

let rakePromise: Thenable<vscode.Task[]> | undefined = undefined;
const taskProvider = vscode.tasks.registerTaskProvider('rake', {
  provideTasks: () => {
    if (!rakePromise) {
      rakePromise = getRakeTasks();
    }
    return rakePromise;
  },
  resolveTask(_task: vscode.Task): vscode.Task | undefined {
		const task = _task.definition.task;
		// A Rake task consists of a task and an optional file as specified in RakeTaskDefinition
		// Make sure that this looks like a Rake task by checking that there is a task.
		if (task) {
			// resolveTask requires that the same definition object be used.
			const definition: RakeTaskDefinition = <any>_task.definition;
			return new vscode.Task(definition, definition.task, 'rake', new vscode.ShellExecution(`rake ${definition.task}`));
		}
		return undefined;  }
});

provideTask처럼, resolveTask 메소드는 VS Code에 의해 호출되어 익스텐션으로 부터 작업을 받습니다. resolveTaskprovideTasks 이후에 호출되며, 이는 provideTasks로 부터 작업을 제공 받지 않는 제공자의 퍼포먼스 향상을 위하여 의도 된 것입니다. 사용자가 개인 작업 제공자를 멈출 수 있게 설정하는 것은 좋은 예시이며, 이는 일반적입니다. 이로 인해 사용자는 특정 제공자로 부터 받은 작업이 느리다는 것을 알 때 해당 제공자를 중지 할 것입니다. 이 경우, 사용자는 여전히 tasks.json에 있는 공급자의 몇몇 작업을 참조 할 수 있습니다. 만약 resolveTask가 구현되지 않았을경우, tasks.json의 작업이 생성 되지 않았다는 경고가 나타날 것입니다. resolveTask를 통해 익스텐션은 tasks.json에 정의 된 작업들을 제공 할 수 있습니다.

getRakeTasks구현은 다음을 행합니다:

  • rake -AT -f Rakefile 커맨드를 사용하여 Rakefile에 정의된 모든 rake 작업을 목록으로 만듬.
  • stdio 출력을 파싱
  • 모든 작업 목록에 대하여, vscode.Task구현을 생성.

Rake 작업 인스턴스화에는 package.json파일에 정의된 작업정의가 필요하기 때문에, VS Code는 아래와 같은 타입스크립트 인터페이스를 사용하여 구조를 정의 할 것입니다:

interface RakeTaskDefinition extends vscode.TaskDefinition {
  /**
   * The task name
   */
  task: string;

  /**
   * The rake file containing the task
   */
  file?: string;
}

출력이 compile이라는 작업에서 나온다고 할 때, 해당 작업 생성은 다음과 같습니다:

let task = new vscode.Task(
  { type: 'rake', task: 'compile' },
  'compile',
  'rake',
  new vscode.ShellExecution('rake compile')
);

출력 안의 모든 작업 목록에 대하여, 해당 VS Code 작업이 위의 패턴을 사용하여 생성되고 getRakeTasks 호출로 인한 모든 작업의 배열이 반환됩니다.

ShellExecution은 각 OS에 해당하는 쉘의 rake compile 커맨드를 실행합니다 (예를 들어 Windows의 경우는 PowerShell에서, Ubuntu는 bash에서 실행됩니다). 만약 작업이 프로세스를 직접 실행해야 한다면 ( 쉘을 생성하지 않고 ), vscode.ProcessExecution이 사용 될 수 있습니다. ProcessExecution은 익스텐션이 프로세스에 전달된 변수를 완전히 제어 할 수 있다는 장점이 있습니다. ShellExecution을 사용하는것은 쉘 커맨드 인터프리테이션을 (bash에서의 wildcard 확장같은) 사용 할 수 있게 합니다. 만약 ShellExecution이 하나의 커맨드 라인에서 생성되었다면, 익스텐션은 적절한 처리가 된 커맨드를 필요로 합니다.(예를 들면 공백을 다루거나 따옴표 같은)

커스텀실행

일반적인 경우, 단순한 ShellExecution이나 ProcessExecution을 사용하는것이 좋습니다. 하지만, 만약 여러분의 작업이 실행 중에 있어 많은 저장 상태를 요구하거나, 분리된 스크립트나 프로세스로서 작동하지 않거나, 많은 양의 출력을 처리해야 한다면 CustomExecution을 사용해야 합니다. CustomExecution은 보통 복잡한 빌드 시스템에 쓰입니다. 이는 작업이 할 수 있는 것에 굉장한 유연성을 허용하지만 동시에, 작업 제공자가 어떤 프로세스 관리와, 출력 파싱에도 책임이 있다는 것을 의미합니다. 또한 작업 제공자는 Pseudoterminal 을 구현해야하고 이를 CustomExecution콜백으로부터 리턴해야합니다.

return new vscode.Task(definition, vscode.TaskScope.Workspace, `${flavor} ${flags.join(' ')}`,
  CustomBuildTaskProvider.CustomBuildScriptType, new vscode.CustomExecution(async (): Promise<vscode.Pseudoterminal> => {
    // When the task is executed, this callback will run. Here, we setup for running the task.
    return new CustomBuildTaskTerminal(this.workspaceRoot, flavor, flags, () => this.sharedState, (state: string) => this.sharedState = state);
  }));

Pseudoterminal의 구현을 포함한 완전한 예시는 https://github.com/Microsoft/vscode-extension-samples/tree/master/task-provider-sample/src/customTaskProvider.ts 에 있습니다.