vscode-languageclient
Version:
VSCode Language client implementation
703 lines (702 loc) • 31.8 kB
JavaScript
"use strict";
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DidSaveTextDocumentFeature = exports.WillSaveWaitUntilFeature = exports.WillSaveFeature = exports.DidChangeTextDocumentFeature = exports.DidCloseTextDocumentFeature = exports.DidOpenTextDocumentFeature = void 0;
const vscode_1 = require("vscode");
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const features_1 = require("./features");
const UUID = __importStar(require("./utils/uuid"));
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
class DidOpenTextDocumentFeature extends features_1.TextDocumentEventFeature {
_syncedDocuments;
_pendingOpenNotifications;
_delayOpen;
_pendingOpenListeners;
constructor(client, syncedDocuments) {
super(client, vscode_1.workspace.onDidOpenTextDocument, vscode_languageserver_protocol_1.DidOpenTextDocumentNotification.type, () => client.middleware.didOpen, (textDocument) => client.code2ProtocolConverter.asOpenTextDocumentParams(textDocument), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
this._syncedDocuments = syncedDocuments;
this._pendingOpenNotifications = new Map();
this._delayOpen = client.clientOptions.textSynchronization?.delayOpenNotifications ?? false;
}
async callback(document) {
if (!this._delayOpen) {
return super.callback(document);
}
else {
if (!this.matches(document)) {
return;
}
const visibleDocuments = this._client.visibleDocuments;
if (visibleDocuments.isVisible(document)) {
return super.callback(document);
}
else {
// Snapshot the text document so that when we send the delayed
// notification it is based on the content/version at the time
// it would've been sent, and not the updated version.
//
// See https://github.com/microsoft/vscode-languageserver-node/issues/1695
const snapshot = new TextDocumentSnapshot(document);
this._pendingOpenNotifications.set(snapshot.uri.toString(), snapshot);
}
}
}
get openDocuments() {
return this._syncedDocuments.values();
}
fillClientCapabilities(capabilities) {
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.openClose) {
this.register({ id: UUID.generateUuid(), registerOptions: { documentSelector: documentSelector } });
}
}
get registrationType() {
return vscode_languageserver_protocol_1.DidOpenTextDocumentNotification.type;
}
register(data) {
super.register(data);
if (!data.registerOptions.documentSelector) {
return;
}
const documentSelector = this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector);
vscode_1.workspace.textDocuments.forEach((textDocument) => {
const uri = textDocument.uri.toString();
if (this._syncedDocuments.has(uri)) {
return;
}
if (vscode_1.languages.match(documentSelector, textDocument) > 0 && !this._client.hasDedicatedTextSynchronizationFeature(textDocument)) {
const visibleDocuments = this._client.visibleDocuments;
if (visibleDocuments.isVisible(textDocument)) {
const middleware = this._client.middleware;
const didOpen = (textDocument) => {
return this._client.sendNotification(this._type, this._createParams(textDocument));
};
(middleware.didOpen ? middleware.didOpen(textDocument, didOpen) : didOpen(textDocument)).catch((error) => {
this._client.error(`Sending document notification ${this._type.method} failed`, error);
});
this._syncedDocuments.set(uri, textDocument);
}
else {
this._pendingOpenNotifications.set(uri, textDocument);
}
}
});
if (this._delayOpen && this._pendingOpenListeners === undefined) {
this._pendingOpenListeners = [];
const visibleDocuments = this._client.visibleDocuments;
this._pendingOpenListeners.push(visibleDocuments.onClose((closed) => {
for (const uri of closed) {
this._pendingOpenNotifications.delete(uri.toString());
}
}));
this._pendingOpenListeners.push(visibleDocuments.onOpen((opened) => {
for (const uri of opened) {
const document = this._pendingOpenNotifications.get(uri.toString());
if (document !== undefined) {
super.callback(document).catch(((error) => {
this._client.error(`Sending document notification ${this._type.method} failed`, error);
}));
this._pendingOpenNotifications.delete(uri.toString());
}
}
}));
this._pendingOpenListeners.push(vscode_1.workspace.onDidCloseTextDocument((document) => {
this._pendingOpenNotifications.delete(document.uri.toString());
}));
}
}
/**
* Sends any pending open notifications unless they are for the document
* being closed.
*
* @param closingDocument The document being closed.
* @returns Whether a pending open notification was dropped because it was
* for the closing document.
*/
async sendPendingOpenNotifications(closingDocument) {
const notifications = Array.from(this._pendingOpenNotifications.values());
this._pendingOpenNotifications.clear();
let didDropOpenNotification = false;
for (const notification of notifications) {
if (closingDocument !== undefined && notification.uri.toString() === closingDocument) {
didDropOpenNotification = true;
continue;
}
await super.callback(notification);
}
return didDropOpenNotification;
}
getTextDocument(data) {
return data;
}
notificationSent(textDocument, type, params) {
this._syncedDocuments.set(textDocument.uri.toString(), textDocument);
super.notificationSent(textDocument, type, params);
}
clear() {
this._pendingOpenNotifications.clear();
if (this._pendingOpenListeners !== undefined) {
for (const listener of this._pendingOpenListeners) {
listener.dispose();
}
this._pendingOpenListeners = undefined;
}
super.clear();
}
}
exports.DidOpenTextDocumentFeature = DidOpenTextDocumentFeature;
class DidCloseTextDocumentFeature extends features_1.TextDocumentEventFeature {
_syncedDocuments;
_pendingTextDocumentChanges;
constructor(client, syncedDocuments, pendingTextDocumentChanges) {
super(client, vscode_1.workspace.onDidCloseTextDocument, vscode_languageserver_protocol_1.DidCloseTextDocumentNotification.type, () => client.middleware.didClose, (textDocument) => client.code2ProtocolConverter.asCloseTextDocumentParams(textDocument), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
this._syncedDocuments = syncedDocuments;
this._pendingTextDocumentChanges = pendingTextDocumentChanges;
}
get registrationType() {
return vscode_languageserver_protocol_1.DidCloseTextDocumentNotification.type;
}
fillClientCapabilities(capabilities) {
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.openClose) {
this.register({ id: UUID.generateUuid(), registerOptions: { documentSelector: documentSelector } });
}
}
async callback(data) {
await super.callback(data);
this._pendingTextDocumentChanges.delete(data.uri.toString());
}
getTextDocument(data) {
return data;
}
notificationSent(textDocument, type, params) {
this._syncedDocuments.delete(textDocument.uri.toString());
super.notificationSent(textDocument, type, params);
}
unregister(id) {
const selector = this._selectors.get(id);
if (selector === undefined) {
return;
}
// The super call removed the selector from the map
// of selectors.
super.unregister(id);
const selectors = this._selectors.values();
this._syncedDocuments.forEach((textDocument) => {
if (vscode_1.languages.match(selector, textDocument) > 0 && !this._selectorFilter(selectors, textDocument) && !this._client.hasDedicatedTextSynchronizationFeature(textDocument)) {
const middleware = this._client.middleware;
const didClose = (textDocument) => {
return this._client.sendNotification(this._type, this._createParams(textDocument));
};
this._syncedDocuments.delete(textDocument.uri.toString());
(middleware.didClose ? middleware.didClose(textDocument, didClose) : didClose(textDocument)).catch((error) => {
this._client.error(`Sending document notification ${this._type.method} failed`, error);
});
}
});
}
}
exports.DidCloseTextDocumentFeature = DidCloseTextDocumentFeature;
class DidChangeTextDocumentFeature extends features_1.DynamicDocumentFeature {
_listener;
_changeData;
_onAboutToSendNotification;
_onNotificationSent;
_onPendingChangeAdded;
_pendingTextDocumentChanges;
_syncKind;
constructor(client, pendingTextDocumentChanges) {
super(client);
this._changeData = new Map();
this._onAboutToSendNotification = new vscode_1.EventEmitter();
this._onNotificationSent = new vscode_1.EventEmitter();
this._onPendingChangeAdded = new vscode_1.EventEmitter();
this._pendingTextDocumentChanges = pendingTextDocumentChanges;
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
}
get onAboutToSendNotification() {
return this._onAboutToSendNotification.event;
}
get onNotificationSent() {
return this._onNotificationSent.event;
}
get onPendingChangeAdded() {
return this._onPendingChangeAdded.event;
}
get syncKind() {
return this._syncKind;
}
get registrationType() {
return vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type;
}
fillClientCapabilities(capabilities) {
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').dynamicRegistration = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.change !== undefined && textDocumentSyncOptions.change !== vscode_languageserver_protocol_1.TextDocumentSyncKind.None) {
this.register({
id: UUID.generateUuid(),
registerOptions: Object.assign({}, { documentSelector: documentSelector }, { syncKind: textDocumentSyncOptions.change })
});
}
}
register(data) {
if (!data.registerOptions.documentSelector) {
return;
}
if (!this._listener) {
this._listener = vscode_1.workspace.onDidChangeTextDocument(this.callback, this);
}
this._changeData.set(data.id, {
syncKind: data.registerOptions.syncKind,
documentSelector: this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector),
});
this.updateSyncKind(data.registerOptions.syncKind);
}
*getDocumentSelectors() {
for (const data of this._changeData.values()) {
yield data.documentSelector;
}
}
async callback(event) {
// Text document changes are send for dirty changes as well. We don't
// have dirty / un-dirty events in the LSP so we ignore content changes
// with length zero.
if (event.contentChanges.length === 0) {
return;
}
// We need to capture the URI and version here since they might change on the text document
// until we reach did `didChange` call since the middleware support async execution.
const uri = event.document.uri;
const version = event.document.version;
const promises = [];
for (const changeData of this._changeData.values()) {
if (vscode_1.languages.match(changeData.documentSelector, event.document) > 0 && !this._client.hasDedicatedTextSynchronizationFeature(event.document)) {
const middleware = this._client.middleware;
if (changeData.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental) {
const didChange = async (event) => {
const params = this._client.code2ProtocolConverter.asChangeTextDocumentParams(event, uri, version);
this.aboutToSendNotification(event.document, vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
await this._client.sendNotification(vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
this.notificationSent(event.document, vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type, params);
};
promises.push(middleware.didChange ? middleware.didChange(event, event => didChange(event)) : didChange(event));
}
else if (changeData.syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
const didChange = async (event) => {
const eventUri = event.document.uri.toString();
this._pendingTextDocumentChanges.set(eventUri, event.document);
this._onPendingChangeAdded.fire();
};
promises.push(middleware.didChange ? middleware.didChange(event, event => didChange(event)) : didChange(event));
}
}
}
return Promise.all(promises).then(undefined, (error) => {
this._client.error(`Sending document notification ${vscode_languageserver_protocol_1.DidChangeTextDocumentNotification.type.method} failed`, error);
throw error;
});
}
aboutToSendNotification(textDocument, type, params) {
this._onAboutToSendNotification.fire({ textDocument, type, params });
}
notificationSent(textDocument, type, params) {
this._onNotificationSent.fire({ textDocument, type, params });
}
unregister(id) {
this._changeData.delete(id);
if (this._changeData.size === 0) {
if (this._listener) {
this._listener.dispose();
this._listener = undefined;
}
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
}
else {
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
for (const changeData of this._changeData.values()) {
this.updateSyncKind(changeData.syncKind);
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
break;
}
}
}
}
clear() {
this._pendingTextDocumentChanges.clear();
this._changeData.clear();
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.None;
if (this._listener) {
this._listener.dispose();
this._listener = undefined;
}
}
getPendingDocumentChanges(excludes) {
if (this._pendingTextDocumentChanges.size === 0) {
return [];
}
let result;
if (excludes.size === 0) {
result = Array.from(this._pendingTextDocumentChanges.values());
this._pendingTextDocumentChanges.clear();
}
else {
result = [];
for (const entry of this._pendingTextDocumentChanges) {
if (!excludes.has(entry[0])) {
result.push(entry[1]);
this._pendingTextDocumentChanges.delete(entry[0]);
}
}
}
return result;
}
getProvider(document) {
for (const changeData of this._changeData.values()) {
if (vscode_1.languages.match(changeData.documentSelector, document) > 0) {
return {
send: (event) => {
return this.callback(event);
}
};
}
}
return undefined;
}
updateSyncKind(syncKind) {
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.Full) {
return;
}
switch (syncKind) {
case vscode_languageserver_protocol_1.TextDocumentSyncKind.Full:
this._syncKind = syncKind;
break;
case vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental:
if (this._syncKind === vscode_languageserver_protocol_1.TextDocumentSyncKind.None) {
this._syncKind = vscode_languageserver_protocol_1.TextDocumentSyncKind.Incremental;
}
break;
}
}
}
exports.DidChangeTextDocumentFeature = DidChangeTextDocumentFeature;
class WillSaveFeature extends features_1.TextDocumentEventFeature {
constructor(client) {
super(client, vscode_1.workspace.onWillSaveTextDocument, vscode_languageserver_protocol_1.WillSaveTextDocumentNotification.type, () => client.middleware.willSave, (willSaveEvent) => client.code2ProtocolConverter.asWillSaveTextDocumentParams(willSaveEvent), (event) => event.document, (selectors, willSaveEvent) => features_1.TextDocumentEventFeature.textDocumentFilter(selectors, willSaveEvent.document));
}
get registrationType() {
return vscode_languageserver_protocol_1.WillSaveTextDocumentNotification.type;
}
fillClientCapabilities(capabilities) {
const value = (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization');
value.willSave = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.willSave) {
this.register({
id: UUID.generateUuid(),
registerOptions: { documentSelector: documentSelector }
});
}
}
getTextDocument(data) {
return data.document;
}
}
exports.WillSaveFeature = WillSaveFeature;
class WillSaveWaitUntilFeature extends features_1.DynamicDocumentFeature {
_listener;
_selectors;
constructor(client) {
super(client);
this._selectors = new Map();
}
getDocumentSelectors() {
return this._selectors.values();
}
get registrationType() {
return vscode_languageserver_protocol_1.WillSaveTextDocumentWaitUntilRequest.type;
}
fillClientCapabilities(capabilities) {
const value = (0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization');
value.willSaveWaitUntil = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.willSaveWaitUntil) {
this.register({
id: UUID.generateUuid(),
registerOptions: { documentSelector: documentSelector }
});
}
}
register(data) {
if (!data.registerOptions.documentSelector) {
return;
}
if (!this._listener) {
this._listener = vscode_1.workspace.onWillSaveTextDocument(this.callback, this);
}
this._selectors.set(data.id, this._client.protocol2CodeConverter.asDocumentSelector(data.registerOptions.documentSelector));
}
callback(event) {
if (features_1.TextDocumentEventFeature.textDocumentFilter(this._selectors.values(), event.document) && !this._client.hasDedicatedTextSynchronizationFeature(event.document)) {
const middleware = this._client.middleware;
const willSaveWaitUntil = (event) => {
return this._client.sendRequest(vscode_languageserver_protocol_1.WillSaveTextDocumentWaitUntilRequest.type, this._client.code2ProtocolConverter.asWillSaveTextDocumentParams(event)).then(async (edits) => {
const vEdits = await this._client.protocol2CodeConverter.asTextEdits(edits);
return vEdits === undefined ? [] : vEdits;
});
};
event.waitUntil(middleware.willSaveWaitUntil
? middleware.willSaveWaitUntil(event, willSaveWaitUntil)
: willSaveWaitUntil(event));
}
}
unregister(id) {
this._selectors.delete(id);
if (this._selectors.size === 0 && this._listener) {
this._listener.dispose();
this._listener = undefined;
}
}
clear() {
this._selectors.clear();
if (this._listener) {
this._listener.dispose();
this._listener = undefined;
}
}
}
exports.WillSaveWaitUntilFeature = WillSaveWaitUntilFeature;
class DidSaveTextDocumentFeature extends features_1.TextDocumentEventFeature {
_includeText;
constructor(client) {
super(client, vscode_1.workspace.onDidSaveTextDocument, vscode_languageserver_protocol_1.DidSaveTextDocumentNotification.type, () => client.middleware.didSave, (textDocument) => client.code2ProtocolConverter.asSaveTextDocumentParams(textDocument, this._includeText), (data) => data, features_1.TextDocumentEventFeature.textDocumentFilter);
this._includeText = false;
}
get registrationType() {
return vscode_languageserver_protocol_1.DidSaveTextDocumentNotification.type;
}
fillClientCapabilities(capabilities) {
(0, features_1.ensure)((0, features_1.ensure)(capabilities, 'textDocument'), 'synchronization').didSave = true;
}
initialize(capabilities, documentSelector) {
const textDocumentSyncOptions = capabilities.resolvedTextDocumentSync;
if (documentSelector && textDocumentSyncOptions && textDocumentSyncOptions.save) {
const saveOptions = typeof textDocumentSyncOptions.save === 'boolean'
? { includeText: false }
: { includeText: !!textDocumentSyncOptions.save.includeText };
this.register({
id: UUID.generateUuid(),
registerOptions: Object.assign({}, { documentSelector: documentSelector }, saveOptions)
});
}
}
register(data) {
this._includeText = !!data.registerOptions.includeText;
super.register(data);
}
getTextDocument(data) {
return data;
}
}
exports.DidSaveTextDocumentFeature = DidSaveTextDocumentFeature;
// Copied from https://github.com/microsoft/vscode/src/vs/editor/common/core/wordHelper.ts
const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?';
/**
* Create a word definition regular expression based on default word separators.
* Optionally provide allowed separators that should be included in words.
*
* The default would look like this:
* /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
*/
function createWordRegExp(allowInWords = '') {
let source = '(-?\\d*\\.\\d\\w*)|([^';
for (const sep of USUAL_WORD_SEPARATORS) {
if (allowInWords.indexOf(sep) >= 0) {
continue;
}
source += '\\' + sep;
}
source += '\\s]+)';
return new RegExp(source, 'g');
}
// catches numbers (including floating numbers) in the first group, and alphanum in the second
const DEFAULT_WORD_REGEXP = createWordRegExp();
class TextDocumentSnapshot {
_extTextDocument;
_capturedTextDocument;
_content;
_uri;
_fileName;
_languageId;
_version;
_eol;
_isUntitled;
_encoding;
_isDirty;
_isClosed;
constructor(textDocument) {
this._extTextDocument = textDocument;
this._content = textDocument.getText();
this._uri = textDocument.uri;
this._fileName = textDocument.fileName;
this._languageId = textDocument.languageId;
this._version = textDocument.version;
this._eol = textDocument.eol;
this._isUntitled = textDocument.isUntitled;
this._encoding = textDocument.encoding;
this._isDirty = textDocument.isDirty;
this._isClosed = textDocument.isClosed;
this._capturedTextDocument = vscode_languageserver_textdocument_1.TextDocument.create(this._uri.toString(), this._languageId, this._version, this._content);
}
get uri() {
return this._uri;
}
get languageId() {
return this._languageId;
}
get version() {
return this._version;
}
get eol() {
return this._eol;
}
get isUntitled() {
return this._isUntitled;
}
get encoding() {
return this._encoding;
}
get fileName() {
return this._fileName;
}
get isDirty() {
return this._isDirty;
}
get isClosed() {
return this._isClosed;
}
save() {
return this.version === this._extTextDocument.version
? this._extTextDocument.save()
: Promise.resolve(false);
}
get lineCount() {
return this._capturedTextDocument.lineCount;
}
offsetAt(position) {
return this._capturedTextDocument.offsetAt(position);
}
positionAt(offset) {
const position = this._capturedTextDocument.positionAt(offset);
return new vscode_1.Position(position.line, position.character);
}
getText(range) {
return this._capturedTextDocument.getText(range);
}
lineAt(lineOrPosition) {
const line = typeof lineOrPosition === 'number' ? lineOrPosition : this.validatePosition(lineOrPosition).line;
if (line < 0 || line >= this.lineCount) {
throw new RangeError(`Illegal value for line: ${line}`);
}
const lineRange = this._capturedTextDocument.getLineRange(line);
const text = this._capturedTextDocument.getText(lineRange);
const firstNonWhitespaceCharacterIndex = text.search(/\S/);
const range = new vscode_1.Range(lineRange.start.line, lineRange.start.character, lineRange.end.line, lineRange.end.character);
const rangeIncludingLineBreak = line + 1 < this.lineCount
? new vscode_1.Range(range.start.line, range.start.character, line + 1, 0)
: range;
return {
lineNumber: line,
text,
range,
rangeIncludingLineBreak,
firstNonWhitespaceCharacterIndex: firstNonWhitespaceCharacterIndex === -1 ? text.length : firstNonWhitespaceCharacterIndex,
isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === -1
};
}
getWordRangeAtPosition(position, regex) {
const lineNumber = this.validatePosition(position).line;
const lineText = this.lineAt(lineNumber).text;
const wordRegex = TextDocumentSnapshot.getWordRegExp(regex);
let match;
wordRegex.lastIndex = 0;
while ((match = wordRegex.exec(lineText)) !== null) {
if (match.index <= position.character && wordRegex.lastIndex >= position.character) {
return new vscode_1.Range(lineNumber, match.index, lineNumber, wordRegex.lastIndex);
}
}
return undefined;
}
validateRange(range) {
const start = this.validatePosition(range.start);
const end = this.validatePosition(range.end);
if (start === range.start && end === range.end) {
return range;
}
return new vscode_1.Range(start.line, start.character, end.line, end.character);
}
validatePosition(position) {
const line = Math.min(Math.max(position.line, 0), this.lineCount - 1);
const lineRange = this._capturedTextDocument.getLineRange(line);
const character = Math.min(Math.max(position.character, 0), lineRange.end.character);
if (line === position.line && character === position.character) {
return position;
}
return new vscode_1.Position(line, character);
}
static getWordRegExp(regex) {
const result = regex ?? DEFAULT_WORD_REGEXP;
if (result.flags.includes('g')) {
return result;
}
const flags = `${result.flags}g`;
return new RegExp(result.source, flags);
}
}