124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
|
|
/*---------------------------------------------------------------------------------------------
|
||
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||
|
|
*--------------------------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
import * as vscode from 'vscode';
|
||
|
|
import { window, workspace } from 'vscode';
|
||
|
|
import { detectLanguage } from '../../lib/src/language/languageDetection';
|
||
|
|
import { CopilotTextDocument, INotebookCell, INotebookDocument, ITextDocument } from '../../lib/src/textDocument';
|
||
|
|
import { TextDocumentManager, WorkspaceFoldersChangeEvent } from '../../lib/src/textDocumentManager';
|
||
|
|
import { transformEvent } from '../../lib/src/util/event';
|
||
|
|
import { normalizeUri } from '../../lib/src/util/uri';
|
||
|
|
|
||
|
|
// List of document URI schemes that avoid ghost text suggestions
|
||
|
|
const ignoreUriSchemes = new Set([
|
||
|
|
'output', // vscode output pane (important: avoids infinite log loop)
|
||
|
|
'search-editor', // search results virtual document
|
||
|
|
'comment', // very little context available and suggestions are often bad
|
||
|
|
'git', // virtual file tracked by git
|
||
|
|
'chat-editing-snapshot-text-model', // VS Code Chat temporary editing snapshot
|
||
|
|
]);
|
||
|
|
|
||
|
|
export function wrapDoc(doc: vscode.TextDocument): ITextDocument | undefined {
|
||
|
|
if (ignoreUriSchemes.has(doc.uri.scheme)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
let text: string;
|
||
|
|
try {
|
||
|
|
text = doc.getText();
|
||
|
|
} catch (e) {
|
||
|
|
// "Invalid string length", it's too big to fit in a string
|
||
|
|
if (e instanceof RangeError) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
throw e;
|
||
|
|
}
|
||
|
|
const languageId = detectLanguage({ uri: doc.uri.toString(), languageId: doc.languageId });
|
||
|
|
return CopilotTextDocument.create(doc.uri.toString(), doc.languageId, doc.version, text, languageId);
|
||
|
|
}
|
||
|
|
|
||
|
|
export class ExtensionTextDocumentManager extends TextDocumentManager {
|
||
|
|
override onDidFocusTextDocument = transformEvent(window.onDidChangeActiveTextEditor, event => {
|
||
|
|
return { document: event && { uri: event.document.uri.toString() } };
|
||
|
|
});
|
||
|
|
|
||
|
|
override onDidChangeTextDocument = transformEvent(workspace.onDidChangeTextDocument, e => {
|
||
|
|
const document = wrapDoc(e.document);
|
||
|
|
return document && { document, contentChanges: e.contentChanges };
|
||
|
|
});
|
||
|
|
|
||
|
|
override onDidOpenTextDocument = transformEvent(workspace.onDidOpenTextDocument, e => {
|
||
|
|
// use wrapDoc() to handle the "Invalid string length" case
|
||
|
|
const text = wrapDoc(e)?.getText();
|
||
|
|
if (text === undefined) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
return { document: { uri: e.uri.toString(), languageId: e.languageId, version: e.version, text } };
|
||
|
|
});
|
||
|
|
|
||
|
|
override onDidCloseTextDocument = transformEvent(workspace.onDidCloseTextDocument, e => {
|
||
|
|
return { document: { uri: normalizeUri(e.uri.toString()) } };
|
||
|
|
});
|
||
|
|
|
||
|
|
override onDidChangeWorkspaceFolders = transformEvent(
|
||
|
|
workspace.onDidChangeWorkspaceFolders,
|
||
|
|
(e): WorkspaceFoldersChangeEvent => {
|
||
|
|
return {
|
||
|
|
workspaceFolders: this.getWorkspaceFolders(),
|
||
|
|
added: e.added.map(f => ({ uri: f.uri.toString(), name: f.name })),
|
||
|
|
removed: e.removed.map(f => ({ uri: f.uri.toString(), name: f.name })),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
getTextDocumentsUnsafe(): ITextDocument[] {
|
||
|
|
const docs: ITextDocument[] = [];
|
||
|
|
for (const vscodeDoc of workspace.textDocuments) {
|
||
|
|
const doc = wrapDoc(vscodeDoc);
|
||
|
|
if (doc) {
|
||
|
|
docs.push(doc);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return docs;
|
||
|
|
}
|
||
|
|
|
||
|
|
findNotebook(doc: { uri: string }): INotebookDocument | undefined {
|
||
|
|
for (const notebook of workspace.notebookDocuments) {
|
||
|
|
if (notebook.getCells().some(cell => cell.document.uri.toString() === doc.uri.toString())) {
|
||
|
|
return {
|
||
|
|
getCells: () => notebook.getCells().map(cell => this.wrapCell(cell)),
|
||
|
|
getCellFor: ({ uri }: { uri: string }) => {
|
||
|
|
const cell = notebook.getCells().find(cell => cell.document.uri.toString() === uri.toString());
|
||
|
|
return cell ? this.wrapCell(cell) : undefined;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
wrapCell(cell: vscode.NotebookCell): INotebookCell {
|
||
|
|
return {
|
||
|
|
...cell,
|
||
|
|
get document(): ITextDocument {
|
||
|
|
return CopilotTextDocument.create(
|
||
|
|
cell.document.uri.toString(),
|
||
|
|
cell.document.languageId,
|
||
|
|
cell.document.version,
|
||
|
|
cell.document.getText(),
|
||
|
|
// use the original language id as cells have no metadata to leverage for language detection
|
||
|
|
cell.document.languageId
|
||
|
|
);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
getWorkspaceFolders() {
|
||
|
|
return (
|
||
|
|
workspace.workspaceFolders?.map(f => {
|
||
|
|
return { uri: f.uri.toString(), name: f.name };
|
||
|
|
}) ?? []
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|