다음을 통해 공유


편집기에서 텍스트 변경

편집, 즉, Visual Studio 편집기에서 열린 텍스트 문서의 변경 내용은 Visual Studio의 사용자 상호 작용 또는 언어 서비스 및 기타 확장의 프로그래밍 방식 변경으로 인해 발생할 수 있습니다. 실시간으로 발생하는 문서 텍스트의 변경 내용을 처리하도록 확장이 준비되어야 합니다.

기본 Visual Studio IDE 프로세스 외부에서 실행되는 확장은 비동기 디자인 패턴을 사용하여 Visual Studio IDE 프로세스와 통신합니다. 즉, C#의 async 키워드로 표시되고 메서드 이름에 Async 접미사로 강화된 비동기 메서드 호출을 사용합니다. 비동기성은 사용자 작업에 응답할 것으로 예상되는 편집기의 컨텍스트에서 상당한 이점입니다. 기존 동기 API 호출은 예상보다 오래 걸리는 경우 사용자 입력에 대한 응답을 중지하고 API 호출이 완료될 때까지 지속되는 UI 고정을 만듭니다. 최신 대화형 애플리케이션에 대한 사용자의 기대는 텍스트 편집기가 항상 응답성을 유지하고 작동을 차단하지 않는다는 것입니다. 따라서 확장을 비동기식으로 사용하는 것은 사용자의 기대를 충족하는 데 필수적입니다.

비동기와 대기를 사용한 비동기 프로그래밍에 대해 자세히 알아보기 비동기 프로그래밍.

새 Visual Studio 확장성 모델에서 확장은 사용자를 기준으로 하는 두 번째 클래스입니다. 편집기 또는 텍스트 문서를 직접 수정할 수 없습니다. 모든 상태 변경은 비동기적이며 협조적이며 Visual Studio IDE는 확장을 대신하여 요청된 변경을 수행합니다. 확장은 문서 또는 텍스트 보기의 특정 버전에 대해 하나 이상의 변경 내용을 요청할 수 있지만 문서의 해당 영역이 변경된 경우와 같이 확장의 변경 내용이 거부될 수 있습니다.

편집은 EditorExtensibilityEditAsync() 메서드를 사용하여 요청됩니다.

레거시 Visual Studio 확장에 익숙한 경우 ITextDocumentEditorITextBufferITextDocument 메서드를 변경하는 상태와 거의 동일하며 대부분의 동일한 기능을 지원합니다.

MutationResult result = await this.Extensibility.Editor().EditAsync(
batch =>
{
    var editor = document.AsEditable(batch);
    editor.Replace(textView.Selection.Extent, newGuidString);
},
cancellationToken);

편집이 잘못되지 않도록 편집기 확장의 편집은 다음과 같이 적용됩니다.

  1. 확장은 문서의 최신 버전에 따라 편집을 요청합니다.
  2. 해당 요청에는 하나 이상의 텍스트 편집, 캐리트 위치 변경 등이 포함될 수 있습니다. ITextViewSnapshotITextDocumentSnapshot포함하여 단일 EditAsync() 요청에서 IEditable 구현하는 모든 형식을 변경할 수 있습니다. 편집은 편집기에서 수행되며, AsEditable()통해 특정 클래스에서 요청할 수 있습니다.
  3. 편집 요청은 Visual Studio IDE로 전송되며, 요청이 만들어진 버전 이후 변경되는 개체가 변경되지 않은 경우에만 성공합니다. 문서가 변경된 경우 변경 내용이 거부될 수 있으므로 확장이 최신 버전에서 다시 시도해야 합니다. 돌연변이 연산의 결과는 result에 저장됩니다.
  4. 편집은 원자성으로 적용되며, 이는 다른 실행 스레드에서 중단되지 않음을 의미합니다. 가장 좋은 방법은 좁은 시간 프레임 내에서 발생하는 모든 변경 내용을 단일 EditAsync() 호출로 수행하여 사용자 편집에서 발생하는 예기치 않은 동작 또는 편집 간에 발생하는 언어 서비스 작업(예: 확장 편집이 캐럿을 이동하는 Roslyn C#과 인터리브되는 경우)을 줄이는 것입니다.

캐리트 위치 변경 또는 확장에서 텍스트 선택

확장에서 텍스트 문서를 편집하면 캐럿 위치에 암시적으로 영향을 줍니다. 예를 들어, 일부 텍스트를 caret에 삽입하면 삽입된 텍스트의 끝으로 캐리트가 이동합니다. 확장은 ITextViewSnapshot.AsEditable().SetSelections() 사용하여 캐럿을 명시적으로 다른 위치로 설정하거나 텍스트를 선택할 수도 있습니다. 예를 들어 다음 코드는 일부 텍스트를 삽입하지만 원래 위치에는 캐리트를 유지합니다.

await this.Extensibility.Editor().EditAsync(batch =>
{
    var caret = textView.Selection.Extent.Start;
    textView.Document.AsEditable(batch).Replace(textView.Selection.Extent, newGuidString);
    textView.AsEditable(batch).SetSelections([new Selection(activePosition: caret, anchorPosition: caret, insertionPosition: caret)]);
},
cancellationToken);

동시 실행

️ ⚠ 편집기 확장은 때때로 동시에 실행될 수 있습니다.

초기 릴리스에는 편집기 확장 코드를 동시에 실행할 수 있는 알려진 문제가 있습니다. 각 비동기 메서드는 올바른 순서로 호출되도록 보장되지만 첫 번째 await 이후의 연속은 인터리브될 수 있습니다. 확장이 실행 순서에 의존하는 경우 이 문제가 해결될 때까지 들어오는 요청 큐를 유지 관리하여 순서를 유지하는 것이 좋습니다.

자세한 내용은 StreamJsonRpc 기본 순서 지정 및 동시성참조하세요.