| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { Emitter } from '../../../base/common/event.js';
- import { Position } from '../core/position.js';
- import { getWordAtText } from '../core/wordHelper.js';
- import { TextModelPart } from './textModelPart.js';
- import { TextModelTokenization } from './textModelTokens.js';
- import { ContiguousTokensStore } from '../tokens/contiguousTokensStore.js';
- import { SparseTokensStore } from '../tokens/sparseTokensStore.js';
- export class TokenizationTextModelPart extends TextModelPart {
- constructor(_languageService, _languageConfigurationService, _textModel, bracketPairsTextModelPart, _languageId) {
- super();
- this._languageService = _languageService;
- this._languageConfigurationService = _languageConfigurationService;
- this._textModel = _textModel;
- this.bracketPairsTextModelPart = bracketPairsTextModelPart;
- this._languageId = _languageId;
- this._onDidChangeLanguage = this._register(new Emitter());
- this.onDidChangeLanguage = this._onDidChangeLanguage.event;
- this._onDidChangeLanguageConfiguration = this._register(new Emitter());
- this.onDidChangeLanguageConfiguration = this._onDidChangeLanguageConfiguration.event;
- this._onDidChangeTokens = this._register(new Emitter());
- this.onDidChangeTokens = this._onDidChangeTokens.event;
- this._backgroundTokenizationState = 0 /* BackgroundTokenizationState.Uninitialized */;
- this._onBackgroundTokenizationStateChanged = this._register(new Emitter());
- this._tokens = new ContiguousTokensStore(this._languageService.languageIdCodec);
- this._semanticTokens = new SparseTokensStore(this._languageService.languageIdCodec);
- this._tokenization = new TextModelTokenization(_textModel, this, this._languageService.languageIdCodec);
- this._languageRegistryListener = this._languageConfigurationService.onDidChange(e => {
- if (e.affects(this._languageId)) {
- this._onDidChangeLanguageConfiguration.fire({});
- }
- });
- }
- acceptEdit(range, text, eolCount, firstLineLength, lastLineLength) {
- this._tokens.acceptEdit(range, eolCount, firstLineLength);
- this._semanticTokens.acceptEdit(range, eolCount, firstLineLength, lastLineLength, text.length > 0 ? text.charCodeAt(0) : 0 /* CharCode.Null */);
- }
- handleDidChangeAttached() {
- this._tokenization.handleDidChangeAttached();
- }
- flush() {
- this._tokens.flush();
- this._semanticTokens.flush();
- }
- handleDidChangeContent(change) {
- this._tokenization.handleDidChangeContent(change);
- }
- dispose() {
- this._languageRegistryListener.dispose();
- this._tokenization.dispose();
- super.dispose();
- }
- get backgroundTokenizationState() {
- return this._backgroundTokenizationState;
- }
- handleTokenizationProgress(completed) {
- if (this._backgroundTokenizationState === 2 /* BackgroundTokenizationState.Completed */) {
- // We already did a full tokenization and don't go back to progressing.
- return;
- }
- const newState = completed ? 2 /* BackgroundTokenizationState.Completed */ : 1 /* BackgroundTokenizationState.InProgress */;
- if (this._backgroundTokenizationState !== newState) {
- this._backgroundTokenizationState = newState;
- this.bracketPairsTextModelPart.handleDidChangeBackgroundTokenizationState();
- this._onBackgroundTokenizationStateChanged.fire();
- }
- }
- setTokens(tokens, backgroundTokenizationCompleted = false) {
- if (tokens.length !== 0) {
- const ranges = [];
- for (let i = 0, len = tokens.length; i < len; i++) {
- const element = tokens[i];
- let minChangedLineNumber = 0;
- let maxChangedLineNumber = 0;
- let hasChange = false;
- for (let lineNumber = element.startLineNumber; lineNumber <= element.endLineNumber; lineNumber++) {
- if (hasChange) {
- this._tokens.setTokens(this._languageId, lineNumber - 1, this._textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), false);
- maxChangedLineNumber = lineNumber;
- }
- else {
- const lineHasChange = this._tokens.setTokens(this._languageId, lineNumber - 1, this._textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), true);
- if (lineHasChange) {
- hasChange = true;
- minChangedLineNumber = lineNumber;
- maxChangedLineNumber = lineNumber;
- }
- }
- }
- if (hasChange) {
- ranges.push({
- fromLineNumber: minChangedLineNumber,
- toLineNumber: maxChangedLineNumber,
- });
- }
- }
- if (ranges.length > 0) {
- this._emitModelTokensChangedEvent({
- tokenizationSupportChanged: false,
- semanticTokensApplied: false,
- ranges: ranges,
- });
- }
- }
- this.handleTokenizationProgress(backgroundTokenizationCompleted);
- }
- setSemanticTokens(tokens, isComplete) {
- this._semanticTokens.set(tokens, isComplete);
- this._emitModelTokensChangedEvent({
- tokenizationSupportChanged: false,
- semanticTokensApplied: tokens !== null,
- ranges: [{ fromLineNumber: 1, toLineNumber: this._textModel.getLineCount() }],
- });
- }
- hasCompleteSemanticTokens() {
- return this._semanticTokens.isComplete();
- }
- hasSomeSemanticTokens() {
- return !this._semanticTokens.isEmpty();
- }
- setPartialSemanticTokens(range, tokens) {
- if (this.hasCompleteSemanticTokens()) {
- return;
- }
- const changedRange = this._textModel.validateRange(this._semanticTokens.setPartial(range, tokens));
- this._emitModelTokensChangedEvent({
- tokenizationSupportChanged: false,
- semanticTokensApplied: true,
- ranges: [
- {
- fromLineNumber: changedRange.startLineNumber,
- toLineNumber: changedRange.endLineNumber,
- },
- ],
- });
- }
- tokenizeViewport(startLineNumber, endLineNumber) {
- startLineNumber = Math.max(1, startLineNumber);
- endLineNumber = Math.min(this._textModel.getLineCount(), endLineNumber);
- this._tokenization.tokenizeViewport(startLineNumber, endLineNumber);
- }
- clearTokens() {
- this._tokens.flush();
- this._emitModelTokensChangedEvent({
- tokenizationSupportChanged: true,
- semanticTokensApplied: false,
- ranges: [
- {
- fromLineNumber: 1,
- toLineNumber: this._textModel.getLineCount(),
- },
- ],
- });
- }
- _emitModelTokensChangedEvent(e) {
- if (!this._textModel._isDisposing()) {
- this.bracketPairsTextModelPart.handleDidChangeTokens(e);
- this._onDidChangeTokens.fire(e);
- }
- }
- resetTokenization() {
- this._tokenization.reset();
- }
- forceTokenization(lineNumber) {
- if (lineNumber < 1 || lineNumber > this._textModel.getLineCount()) {
- throw new Error('Illegal value for lineNumber');
- }
- this._tokenization.forceTokenization(lineNumber);
- }
- isCheapToTokenize(lineNumber) {
- return this._tokenization.isCheapToTokenize(lineNumber);
- }
- tokenizeIfCheap(lineNumber) {
- if (this.isCheapToTokenize(lineNumber)) {
- this.forceTokenization(lineNumber);
- }
- }
- getLineTokens(lineNumber) {
- if (lineNumber < 1 || lineNumber > this._textModel.getLineCount()) {
- throw new Error('Illegal value for lineNumber');
- }
- return this._getLineTokens(lineNumber);
- }
- _getLineTokens(lineNumber) {
- const lineText = this._textModel.getLineContent(lineNumber);
- const syntacticTokens = this._tokens.getTokens(this._languageId, lineNumber - 1, lineText);
- return this._semanticTokens.addSparseTokens(lineNumber, syntacticTokens);
- }
- getTokenTypeIfInsertingCharacter(lineNumber, column, character) {
- const position = this._textModel.validatePosition(new Position(lineNumber, column));
- return this._tokenization.getTokenTypeIfInsertingCharacter(position, character);
- }
- tokenizeLineWithEdit(position, length, newText) {
- const validatedPosition = this._textModel.validatePosition(position);
- return this._tokenization.tokenizeLineWithEdit(validatedPosition, length, newText);
- }
- getLanguageConfiguration(languageId) {
- return this._languageConfigurationService.getLanguageConfiguration(languageId);
- }
- // Having tokens allows implementing additional helper methods
- getWordAtPosition(_position) {
- this.assertNotDisposed();
- const position = this._textModel.validatePosition(_position);
- const lineContent = this._textModel.getLineContent(position.lineNumber);
- const lineTokens = this._getLineTokens(position.lineNumber);
- const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
- // (1). First try checking right biased word
- const [rbStartOffset, rbEndOffset] = TokenizationTextModelPart._findLanguageBoundaries(lineTokens, tokenIndex);
- const rightBiasedWord = getWordAtText(position.column, this.getLanguageConfiguration(lineTokens.getLanguageId(tokenIndex)).getWordDefinition(), lineContent.substring(rbStartOffset, rbEndOffset), rbStartOffset);
- // Make sure the result touches the original passed in position
- if (rightBiasedWord &&
- rightBiasedWord.startColumn <= _position.column &&
- _position.column <= rightBiasedWord.endColumn) {
- return rightBiasedWord;
- }
- // (2). Else, if we were at a language boundary, check the left biased word
- if (tokenIndex > 0 && rbStartOffset === position.column - 1) {
- // edge case, where `position` sits between two tokens belonging to two different languages
- const [lbStartOffset, lbEndOffset] = TokenizationTextModelPart._findLanguageBoundaries(lineTokens, tokenIndex - 1);
- const leftBiasedWord = getWordAtText(position.column, this.getLanguageConfiguration(lineTokens.getLanguageId(tokenIndex - 1)).getWordDefinition(), lineContent.substring(lbStartOffset, lbEndOffset), lbStartOffset);
- // Make sure the result touches the original passed in position
- if (leftBiasedWord &&
- leftBiasedWord.startColumn <= _position.column &&
- _position.column <= leftBiasedWord.endColumn) {
- return leftBiasedWord;
- }
- }
- return null;
- }
- static _findLanguageBoundaries(lineTokens, tokenIndex) {
- const languageId = lineTokens.getLanguageId(tokenIndex);
- // go left until a different language is hit
- let startOffset = 0;
- for (let i = tokenIndex; i >= 0 && lineTokens.getLanguageId(i) === languageId; i--) {
- startOffset = lineTokens.getStartOffset(i);
- }
- // go right until a different language is hit
- let endOffset = lineTokens.getLineContent().length;
- for (let i = tokenIndex, tokenCount = lineTokens.getCount(); i < tokenCount && lineTokens.getLanguageId(i) === languageId; i++) {
- endOffset = lineTokens.getEndOffset(i);
- }
- return [startOffset, endOffset];
- }
- getWordUntilPosition(position) {
- const wordAtPosition = this.getWordAtPosition(position);
- if (!wordAtPosition) {
- return {
- word: '',
- startColumn: position.column,
- endColumn: position.column,
- };
- }
- return {
- word: wordAtPosition.word.substr(0, position.column - wordAtPosition.startColumn),
- startColumn: wordAtPosition.startColumn,
- endColumn: position.column,
- };
- }
- getLanguageId() {
- return this._languageId;
- }
- getLanguageIdAtPosition(lineNumber, column) {
- const position = this._textModel.validatePosition(new Position(lineNumber, column));
- const lineTokens = this.getLineTokens(position.lineNumber);
- return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(position.column - 1));
- }
- setLanguageId(languageId) {
- if (this._languageId === languageId) {
- // There's nothing to do
- return;
- }
- const e = {
- oldLanguage: this._languageId,
- newLanguage: languageId
- };
- this._languageId = languageId;
- this.bracketPairsTextModelPart.handleDidChangeLanguage(e);
- this._tokenization.handleDidChangeLanguage(e);
- this._onDidChangeLanguage.fire(e);
- this._onDidChangeLanguageConfiguration.fire({});
- }
- }
|