Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/common/debugContentProvider.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { URI as uri } from '../../../../base/common/uri.js';
7
import { localize } from '../../../../nls.js';
8
import { getMimeTypes } from '../../../../editor/common/services/languagesAssociations.js';
9
import { ITextModel } from '../../../../editor/common/model.js';
10
import { IModelService } from '../../../../editor/common/services/model.js';
11
import { ILanguageService } from '../../../../editor/common/languages/language.js';
12
import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js';
13
import { IWorkbenchContribution } from '../../../common/contributions.js';
14
import { DEBUG_SCHEME, IDebugService, IDebugSession } from './debug.js';
15
import { Source } from './debugSource.js';
16
import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js';
17
import { EditOperation } from '../../../../editor/common/core/editOperation.js';
18
import { Range } from '../../../../editor/common/core/range.js';
19
import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
20
import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js';
21
import { ErrorNoTelemetry } from '../../../../base/common/errors.js';
22
import { Disposable } from '../../../../base/common/lifecycle.js';
23
24
/**
25
* Debug URI format
26
*
27
* a debug URI represents a Source object and the debug session where the Source comes from.
28
*
29
* debug:arbitrary_path?session=123e4567-e89b-12d3-a456-426655440000&ref=1016
30
* \___/ \____________/ \__________________________________________/ \______/
31
* | | | |
32
* scheme source.path session id source.reference
33
*
34
* the arbitrary_path and the session id are encoded with 'encodeURIComponent'
35
*
36
*/
37
export class DebugContentProvider extends Disposable implements IWorkbenchContribution, ITextModelContentProvider {
38
39
private static INSTANCE: DebugContentProvider;
40
41
private readonly pendingUpdates = new Map<string, CancellationTokenSource>();
42
43
constructor(
44
@ITextModelService textModelResolverService: ITextModelService,
45
@IDebugService private readonly debugService: IDebugService,
46
@IModelService private readonly modelService: IModelService,
47
@ILanguageService private readonly languageService: ILanguageService,
48
@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService
49
) {
50
super();
51
this._store.add(textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this));
52
DebugContentProvider.INSTANCE = this;
53
}
54
55
override dispose(): void {
56
this.pendingUpdates.forEach(cancellationSource => cancellationSource.dispose());
57
super.dispose();
58
}
59
60
provideTextContent(resource: uri): Promise<ITextModel> | null {
61
return this.createOrUpdateContentModel(resource, true);
62
}
63
64
/**
65
* Reload the model content of the given resource.
66
* If there is no model for the given resource, this method does nothing.
67
*/
68
static refreshDebugContent(resource: uri): void {
69
DebugContentProvider.INSTANCE?.createOrUpdateContentModel(resource, false);
70
}
71
72
/**
73
* Create or reload the model content of the given resource.
74
*/
75
private createOrUpdateContentModel(resource: uri, createIfNotExists: boolean): Promise<ITextModel> | null {
76
77
const model = this.modelService.getModel(resource);
78
if (!model && !createIfNotExists) {
79
// nothing to do
80
return null;
81
}
82
83
let session: IDebugSession | undefined;
84
85
if (resource.query) {
86
const data = Source.getEncodedDebugData(resource);
87
session = this.debugService.getModel().getSession(data.sessionId);
88
}
89
90
if (!session) {
91
// fallback: use focused session
92
session = this.debugService.getViewModel().focusedSession;
93
}
94
95
if (!session) {
96
return Promise.reject(new ErrorNoTelemetry(localize('unable', "Unable to resolve the resource without a debug session")));
97
}
98
const createErrModel = (errMsg?: string) => {
99
this.debugService.sourceIsNotAvailable(resource);
100
const languageSelection = this.languageService.createById(PLAINTEXT_LANGUAGE_ID);
101
const message = errMsg
102
? localize('canNotResolveSourceWithError', "Could not load source '{0}': {1}.", resource.path, errMsg)
103
: localize('canNotResolveSource', "Could not load source '{0}'.", resource.path);
104
return this.modelService.createModel(message, languageSelection, resource);
105
};
106
107
return session.loadSource(resource).then(response => {
108
109
if (response && response.body) {
110
111
if (model) {
112
113
const newContent = response.body.content;
114
115
// cancel and dispose an existing update
116
const cancellationSource = this.pendingUpdates.get(model.id);
117
cancellationSource?.cancel();
118
119
// create and keep update token
120
const myToken = new CancellationTokenSource();
121
this.pendingUpdates.set(model.id, myToken);
122
123
// update text model
124
return this.editorWorkerService.computeMoreMinimalEdits(model.uri, [{ text: newContent, range: model.getFullModelRange() }]).then(edits => {
125
126
// remove token
127
this.pendingUpdates.delete(model.id);
128
129
if (!myToken.token.isCancellationRequested && edits && edits.length > 0) {
130
// use the evil-edit as these models show in readonly-editor only
131
model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
132
}
133
return model;
134
});
135
} else {
136
// create text model
137
const mime = response.body.mimeType || getMimeTypes(resource)[0];
138
const languageSelection = this.languageService.createByMimeType(mime);
139
return this.modelService.createModel(response.body.content, languageSelection, resource);
140
}
141
}
142
143
return createErrModel();
144
145
}, (err: DebugProtocol.ErrorResponse) => createErrModel(err.message));
146
}
147
}
148
149