Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin: add LinkedEditingRanges support #11137

Merged
merged 1 commit into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { UriComponents } from './uri-components';
import { CompletionItemTag } from '../plugin/types-impl';
import { Event as TheiaEvent } from '@theia/core/lib/common/event';
import { URI } from '@theia/core/shared/vscode-uri';
import { SerializedRegExp } from './plugin-api-rpc';

// Should contains internal Plugin API types

Expand Down Expand Up @@ -541,6 +542,11 @@ export interface CallHierarchyOutgoingCall {
fromRanges: Range[];
}

export interface LinkedEditingRanges {
ranges: Range[];
wordPattern?: SerializedRegExp;
}

export interface SearchInWorkspaceResult {
root: string;
fileUri: string;
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ import {
CommentThreadCollapsibleState,
CommentThread,
CommentThreadChangedEvent,
CodeActionProviderDocumentation
CodeActionProviderDocumentation,
LinkedEditingRanges
} from './plugin-api-rpc-model';
import { ExtPluginApi } from './plugin-ext-api-contribution';
import { KeysToAnyValues, KeysToKeysToAnyValue } from './types';
Expand Down Expand Up @@ -1483,6 +1484,7 @@ export interface LanguagesExt {
$provideRootDefinition(handle: number, resource: UriComponents, location: Position, token: CancellationToken): Promise<CallHierarchyItem[] | undefined>;
$provideCallers(handle: number, definition: CallHierarchyItem, token: CancellationToken): Promise<CallHierarchyIncomingCall[] | undefined>;
$provideCallees(handle: number, definition: CallHierarchyItem, token: CancellationToken): Promise<CallHierarchyOutgoingCall[] | undefined>;
$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: Position, token: CancellationToken): Promise<LinkedEditingRanges | undefined>;
$releaseCallHierarchy(handle: number, session?: string): Promise<boolean>;
}

Expand Down Expand Up @@ -1531,6 +1533,7 @@ export interface LanguagesMain {
$emitDocumentSemanticTokensEvent(eventHandle: number): void;
$registerDocumentRangeSemanticTokensProvider(handle: number, pluginInfo: PluginInfo, selector: SerializedDocumentFilter[], legend: theia.SemanticTokensLegend): void;
$registerCallHierarchyProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerLinkedEditingRangeProvider(handle: number, selector: SerializedDocumentFilter[]): void;
}

export interface WebviewInitData {
Expand Down
26 changes: 26 additions & 0 deletions packages/plugin-ext/src/main/browser/languages-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,32 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
});
}

// --- linked editing range

$registerLinkedEditingRangeProvider(handle: number, selector: SerializedDocumentFilter[]): void {
const languageSelector = this.toLanguageSelector(selector);
const linkedEditingRangeProvider = this.createLinkedEditingRangeProvider(handle);
this.register(handle,
(monaco.languages.registerLinkedEditingRangeProvider as RegistrationFunction<monaco.languages.LinkedEditingRangeProvider>)(languageSelector, linkedEditingRangeProvider)
);
}

protected createLinkedEditingRangeProvider(handle: number): monaco.languages.LinkedEditingRangeProvider {
return {
provideLinkedEditingRanges: async (model: monaco.editor.ITextModel, position: monaco.Position, token: CancellationToken):
Promise<monaco.languages.LinkedEditingRanges | undefined> => {
const res = await this.proxy.$provideLinkedEditingRanges(handle, model.uri, position, token);
if (res) {
return {
ranges: res.ranges,
wordPattern: reviveRegExp(res.wordPattern)
};
}
return undefined;
}
};
};

}

function reviveMarker(marker: MarkerData): vst.Diagnostic {
Expand Down
55 changes: 55 additions & 0 deletions packages/plugin-ext/src/plugin/languages-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// *****************************************************************************
// Copyright (C) 2022 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************

import * as theia from '@theia/plugin';
import { SerializedIndentationRule, SerializedOnEnterRule, SerializedRegExp } from '../common';

export function serializeEnterRules(rules?: theia.OnEnterRule[]): SerializedOnEnterRule[] | undefined {
if (typeof rules === 'undefined' || rules === null) {
return undefined;
}

return rules.map(r =>
({
action: r.action,
beforeText: serializeRegExp(r.beforeText),
afterText: serializeRegExp(r.afterText)
} as SerializedOnEnterRule));
}

export function serializeRegExp(regexp?: RegExp): SerializedRegExp | undefined {
if (typeof regexp === 'undefined' || regexp === null) {
return undefined;
}

return {
pattern: regexp.source,
flags: (regexp.global ? 'g' : '') + (regexp.ignoreCase ? 'i' : '') + (regexp.multiline ? 'm' : '')
};
}

export function serializeIndentation(indentationRules?: theia.IndentationRule): SerializedIndentationRule | undefined {
if (typeof indentationRules === 'undefined' || indentationRules === null) {
return undefined;
}

return {
increaseIndentPattern: serializeRegExp(indentationRules.increaseIndentPattern),
decreaseIndentPattern: serializeRegExp(indentationRules.decreaseIndentPattern),
indentNextLinePattern: serializeRegExp(indentationRules.indentNextLinePattern),
unIndentedLinePattern: serializeRegExp(indentationRules.unIndentedLinePattern)
};
}
60 changes: 19 additions & 41 deletions packages/plugin-ext/src/plugin/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ import {
PLUGIN_RPC_CONTEXT,
LanguagesMain,
SerializedLanguageConfiguration,
SerializedRegExp,
SerializedOnEnterRule,
SerializedIndentationRule,
Position,
Selection,
RawColorInfo,
Expand Down Expand Up @@ -63,6 +60,7 @@ import {
CallHierarchyItem,
CallHierarchyIncomingCall,
CallHierarchyOutgoingCall,
LinkedEditingRanges,
} from '../common/plugin-api-rpc-model';
import { CompletionAdapter } from './languages/completion';
import { Diagnostics } from './languages/diagnostics';
Expand Down Expand Up @@ -94,6 +92,8 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
import { DocumentSemanticTokensAdapter, DocumentRangeSemanticTokensAdapter } from './languages/semantic-highlighting';
import { isReadonlyArray } from '../common/arrays';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { LinkedEditingRangeAdapter } from './languages/linked-editing-range';
import { serializeEnterRules, serializeIndentation, serializeRegExp } from './languages-utils';

type Adapter = CompletionAdapter |
SignatureHelpAdapter |
Expand All @@ -118,7 +118,8 @@ type Adapter = CompletionAdapter |
RenameAdapter |
CallHierarchyAdapter |
DocumentRangeSemanticTokensAdapter |
DocumentSemanticTokensAdapter;
DocumentSemanticTokensAdapter |
LinkedEditingRangeAdapter;

export class LanguagesExtImpl implements LanguagesExt {

Expand Down Expand Up @@ -630,6 +631,19 @@ export class LanguagesExtImpl implements LanguagesExt {
}
// ### Call Hierarchy Provider end

// ### Linked Editing Range Provider begin
registerLinkedEditingRangeProvider(selector: theia.DocumentSelector, provider: theia.LinkedEditingRangeProvider): theia.Disposable {
const handle = this.addNewAdapter(new LinkedEditingRangeAdapter(this.documents, provider));
this.proxy.$registerLinkedEditingRangeProvider(handle, this.transformDocumentSelector(selector));
return this.createDisposable(handle);
}

$provideLinkedEditingRanges(handle: number, resource: UriComponents, position: Position, token: theia.CancellationToken): Promise<LinkedEditingRanges | undefined> {
return this.withAdapter(handle, LinkedEditingRangeAdapter, async adapter => adapter.provideRanges(URI.revive(resource), position, token), undefined);
}

// ### Linked Editing Range Provider end

// #region semantic coloring

registerDocumentSemanticTokensProvider(selector: theia.DocumentSelector, provider: theia.DocumentSemanticTokensProvider, legend: theia.SemanticTokensLegend,
Expand Down Expand Up @@ -671,43 +685,7 @@ export class LanguagesExtImpl implements LanguagesExt {
// #endregion
}

function serializeEnterRules(rules?: theia.OnEnterRule[]): SerializedOnEnterRule[] | undefined {
if (typeof rules === 'undefined' || rules === null) {
return undefined;
}

return rules.map(r =>
({
action: r.action,
beforeText: serializeRegExp(r.beforeText),
afterText: serializeRegExp(r.afterText)
} as SerializedOnEnterRule));
}

function serializeRegExp(regexp?: RegExp): SerializedRegExp | undefined {
if (typeof regexp === 'undefined' || regexp === null) {
return undefined;
}

return {
pattern: regexp.source,
flags: (regexp.global ? 'g' : '') + (regexp.ignoreCase ? 'i' : '') + (regexp.multiline ? 'm' : '')
};
}

function serializeIndentation(indentationRules?: theia.IndentationRule): SerializedIndentationRule | undefined {
if (typeof indentationRules === 'undefined' || indentationRules === null) {
return undefined;
}

return {
increaseIndentPattern: serializeRegExp(indentationRules.increaseIndentPattern),
decreaseIndentPattern: serializeRegExp(indentationRules.decreaseIndentPattern),
indentNextLinePattern: serializeRegExp(indentationRules.indentNextLinePattern),
unIndentedLinePattern: serializeRegExp(indentationRules.unIndentedLinePattern)
};
}

function getPluginLabel(pluginInfo: PluginInfo): string {
return pluginInfo.displayName || pluginInfo.name;
}

48 changes: 48 additions & 0 deletions packages/plugin-ext/src/plugin/languages/linked-editing-range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// *****************************************************************************
// Copyright (C) 2022 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************

import * as theia from '@theia/plugin';
import * as rpc from '../../common/plugin-api-rpc';
import { DocumentsExtImpl } from '../documents';
import { LinkedEditingRanges } from '../../common/plugin-api-rpc-model';
import { URI } from '@theia/core/shared/vscode-uri';
import { coalesce } from '../../common/arrays';
import { fromRange, toPosition } from '../type-converters';
import { serializeRegExp } from '../languages-utils';

export class LinkedEditingRangeAdapter {

constructor(
private readonly documents: DocumentsExtImpl,
private readonly provider: theia.LinkedEditingRangeProvider
) { }

async provideRanges(resource: URI, position: rpc.Position, token: theia.CancellationToken): Promise<LinkedEditingRanges | undefined> {

const doc = this.documents.getDocument(resource);
const pos = toPosition(position);

const value = await this.provider.provideLinkedEditingRanges(doc, pos, token);
if (value && Array.isArray(value.ranges)) {
return {
ranges: coalesce(value.ranges.map(r => fromRange(r))),
wordPattern: serializeRegExp(value.wordPattern)
};
}
return undefined;
}

}
9 changes: 7 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ import {
SourceControlInputBoxValidationType,
URI,
FileDecoration,
ExtensionMode
ExtensionMode,
LinkedEditingRanges
} from './types-impl';
import { AuthenticationExtImpl } from './authentication-ext';
import { SymbolKind } from '../common/plugin-api-rpc-model';
Expand Down Expand Up @@ -779,6 +780,9 @@ export function createAPIFactory(
},
registerCallHierarchyProvider(selector: theia.DocumentSelector, provider: theia.CallHierarchyProvider): theia.Disposable {
return languagesExt.registerCallHierarchyProvider(selector, provider);
},
registerLinkedEditingRangeProvider(selector: theia.DocumentSelector, provider: theia.LinkedEditingRangeProvider): theia.Disposable {
return languagesExt.registerLinkedEditingRangeProvider(selector, provider);
}
};

Expand Down Expand Up @@ -1035,7 +1039,8 @@ export function createAPIFactory(
SourceControlInputBoxValidationType,
FileDecoration,
CancellationError,
ExtensionMode
ExtensionMode,
LinkedEditingRanges
};
};
}
Expand Down
12 changes: 12 additions & 0 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2486,6 +2486,18 @@ export class CallHierarchyOutgoingCall {
}
}

@es5ClassCompat
export class LinkedEditingRanges {

ranges: theia.Range[];
wordPattern?: RegExp;

constructor(ranges: Range[], wordPattern?: RegExp) {
this.ranges = ranges;
this.wordPattern = wordPattern;
}
}

@es5ClassCompat
export class TimelineItem {
timestamp: number;
Expand Down
Loading