-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathRascalTerminalLinkProvider.ts
More file actions
115 lines (104 loc) · 4.61 KB
/
RascalTerminalLinkProvider.ts
File metadata and controls
115 lines (104 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
* Copyright (c) 2018-2023, NWO-I CWI and Swat.engineering
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import * as vscode from 'vscode';
import { CancellationToken, ProviderResult, TerminalLink, TerminalLinkContext, TerminalLinkProvider } from 'vscode';
import { LanguageClient } from 'vscode-languageclient/node';
import { PositionConverter } from './util/PositionConverter';
interface ExtendedLink extends TerminalLink {
loc: SourceLocation;
}
export interface SourceLocation {
uri: string;
offsetLength?: [number, number];
beginLineColumn?: [number, number];
endLineColumn?: [number, number];
}
/**
* We only detect Source Locations, as normal markdown links are already detected correctly by vscode
*/
export class RascalTerminalLinkProvider implements TerminalLinkProvider<ExtendedLink> {
constructor (private readonly client: Promise<LanguageClient>) {
}
linkDetector() {
// sadly java script regex store state, so we have to create a new one everytime
return new RegExp(
"\\|[^\\t-\\n\\r\\s\\|:]+://[^\\t-\\n\\r\\s\\|]*\\|" // |location|
+ "(\\([^\\)]*\\))?" // (optional offset)
, "g");
}
provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult<ExtendedLink[]> {
if (!context.terminal.name.includes("Rascal")) {
return null;
}
const matcher = this.linkDetector();
let match: RegExpExecArray | null;
const result: ExtendedLink[] = [];
while ((match = matcher.exec(context.line)) !== null && !token.isCancellationRequested) {
result.push(buildLink(match));
}
return result.length === 0 ? null : result;
}
async handleTerminalLink(link: ExtendedLink): Promise<void> {
const sloc:SourceLocation = link.loc;
if (sloc.uri.startsWith("http")) {
return vscode.commands.executeCommand("vscode.open", sloc.uri) ;
}
const rsloc:SourceLocation = await (await this.client).sendRequest("rascal/filesystem/resolveLocation", sloc);
const td = await vscode.workspace.openTextDocument(vscode.Uri.parse(rsloc.uri));
const te = await vscode.window.showTextDocument(td);
const targetRange = PositionConverter.rascalToVSCodeRange(td, rsloc);
if (targetRange) {
te.revealRange(targetRange);
te.selection = new vscode.Selection(
targetRange.start.line,
targetRange.start.character,
targetRange.end.line,
targetRange.end.character,
);
}
}
}
function buildLink(match: RegExpExecArray): ExtendedLink {
const linkMatch = match[0];
const linkOffset = match.index + 1;
const linkLength = linkMatch.indexOf('|', 2);
const sloc = <SourceLocation>{ uri: linkMatch.substring(1, linkLength) };
const numbers = linkMatch.substring(linkLength).match(/\d+/g,);
if (numbers && numbers.length >= 2) {
sloc.offsetLength = [Number(numbers[0]), Number(numbers[1])];
if (numbers.length === 6) {
// we have a full loc
sloc.beginLineColumn = [Number(numbers[2]), Number(numbers[3])];
sloc.endLineColumn = [Number(numbers[4]), Number(numbers[5])];
}
}
return <ExtendedLink>{
startIndex: linkOffset - 1,
length: linkMatch.length,
loc: sloc
};
}