Skip to content

Commit 6fa4631

Browse files
committed
Move project.json dependency completion & hover to vscode-omnisharp
1 parent 3c361af commit 6fa4631

File tree

4 files changed

+414
-4
lines changed

4 files changed

+414
-4
lines changed

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
],
1616
"main": "./out/omnisharpMain",
1717
"scripts": {
18-
"postinstall": "tsc"
18+
"postinstall": "node ./node_modules/vscode/bin/install && tsc"
1919
},
2020
"dependencies": {
2121
"decompress": "^3.0.0",
@@ -24,6 +24,8 @@
2424
"github-releases": "^0.3.0",
2525
"run-in-terminal": "*",
2626
"semver": "*",
27+
"request-light": "^0.1.0",
28+
"jsonc-parser": "^0.2.0",
2729
"vscode-debugprotocol": "^1.6.1",
2830
"vscode-extension-telemetry": "0.0.4",
2931
"tmp": "0.0.28",
@@ -34,15 +36,16 @@
3436
"gulp-tslint": "^4.3.0",
3537
"tslint": "^3.3.0",
3638
"tslint-microsoft-contrib": "^2.0.0",
37-
"typescript": "^1.7.3",
38-
"vscode": "^0.10.1",
39+
"typescript": "^1.8.9",
40+
"vscode": "^0.11.0",
3941
"vsce": "^1.3.0"
4042
},
4143
"engines": {
4244
"vscode": "^0.10.10"
4345
},
4446
"activationEvents": [
4547
"onLanguage:csharp",
48+
"onLanguage:json",
4649
"onCommand:o.restart",
4750
"onCommand:o.pickProjectAndStart",
4851
"onCommand:o.showOutput",
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
'use strict';
6+
7+
import {Location, getLocation, createScanner, SyntaxKind} from 'jsonc-parser';
8+
import {basename} from 'path';
9+
import {ProjectJSONContribution} from './projectJSONContribution';
10+
import {XHRRequest, configure as configureXHR, xhr} from 'request-light';
11+
12+
import {CompletionItem, CompletionItemProvider, CompletionList, TextDocument, Position, Hover, HoverProvider,
13+
CancellationToken, Range, TextEdit, MarkedString, DocumentSelector, languages, workspace, Disposable} from 'vscode';
14+
15+
export interface ISuggestionsCollector {
16+
add(suggestion: CompletionItem): void;
17+
error(message:string): void;
18+
log(message:string): void;
19+
setAsIncomplete(): void;
20+
}
21+
22+
export interface IJSONContribution {
23+
getDocumentSelector(): DocumentSelector;
24+
getInfoContribution(fileName: string, location: Location) : Thenable<MarkedString[]>;
25+
collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast:boolean, result: ISuggestionsCollector) : Thenable<any>;
26+
collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable<any>;
27+
collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable<any>;
28+
resolveSuggestion?(item: CompletionItem): Thenable<CompletionItem>;
29+
}
30+
31+
export function addJSONProviders() : Disposable {
32+
let subscriptions : Disposable[] = [];
33+
34+
// configure the XHR library with the latest proxy settings
35+
function configureHttpRequest() {
36+
let httpSettings = workspace.getConfiguration('http');
37+
configureXHR(httpSettings.get<string>('proxy'), httpSettings.get<boolean>('proxyStrictSSL'));
38+
}
39+
40+
configureHttpRequest();
41+
subscriptions.push(workspace.onDidChangeConfiguration(e => configureHttpRequest()));
42+
43+
// register completion and hove providers for JSON setting file(s)
44+
let contributions = [ new ProjectJSONContribution(xhr) ];
45+
contributions.forEach(contribution => {
46+
let selector = contribution.getDocumentSelector();
47+
subscriptions.push(languages.registerCompletionItemProvider(selector, new JSONCompletionItemProvider(contribution)));
48+
subscriptions.push(languages.registerHoverProvider(selector, new JSONHoverProvider(contribution)));
49+
});
50+
51+
return Disposable.from(...subscriptions);
52+
}
53+
54+
export class JSONHoverProvider implements HoverProvider {
55+
56+
constructor(private jsonContribution: IJSONContribution) {
57+
}
58+
59+
public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable<Hover> {
60+
let fileName = basename(document.fileName);
61+
let offset = document.offsetAt(position);
62+
let location = getLocation(document.getText(), offset);
63+
let node = location.previousNode;
64+
if (node && node.offset <= offset && offset <= node.offset + node.length) {
65+
let promise = this.jsonContribution.getInfoContribution(fileName, location);
66+
if (promise) {
67+
return promise.then(htmlContent => {
68+
let range = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
69+
let result: Hover = {
70+
contents: htmlContent,
71+
range: range
72+
};
73+
return result;
74+
});
75+
}
76+
}
77+
return null;
78+
}
79+
}
80+
81+
export class JSONCompletionItemProvider implements CompletionItemProvider {
82+
83+
constructor(private jsonContribution: IJSONContribution) {
84+
}
85+
86+
public resolveCompletionItem(item: CompletionItem, token: CancellationToken) : Thenable<CompletionItem> {
87+
if (this.jsonContribution.resolveSuggestion) {
88+
let resolver = this.jsonContribution.resolveSuggestion(item);
89+
if (resolver) {
90+
return resolver;
91+
}
92+
}
93+
return Promise.resolve(item);
94+
}
95+
96+
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Thenable<CompletionList> {
97+
98+
let fileName = basename(document.fileName);
99+
100+
let currentWord = this.getCurrentWord(document, position);
101+
let overwriteRange = null;
102+
let items: CompletionItem[] = [];
103+
let isIncomplete = false;
104+
105+
let offset = document.offsetAt(position);
106+
let location = getLocation(document.getText(), offset);
107+
108+
let node = location.previousNode;
109+
if (node && node.offset <= offset && offset <= node.offset + node.length && (node.type === 'property' || node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
110+
overwriteRange = new Range(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
111+
} else {
112+
overwriteRange = new Range(document.positionAt(offset - currentWord.length), position);
113+
}
114+
115+
let proposed: { [key: string]: boolean } = {};
116+
let collector: ISuggestionsCollector = {
117+
add: (suggestion: CompletionItem) => {
118+
if (!proposed[suggestion.label]) {
119+
proposed[suggestion.label] = true;
120+
if (overwriteRange) {
121+
suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText);
122+
}
123+
124+
items.push(suggestion);
125+
}
126+
},
127+
setAsIncomplete: () => isIncomplete = true,
128+
error: (message: string) => console.error(message),
129+
log: (message: string) => console.log(message)
130+
};
131+
132+
let collectPromise : Thenable<any> = null;
133+
134+
if (location.isAtPropertyKey) {
135+
let addValue = !location.previousNode || !location.previousNode.columnOffset && (offset == (location.previousNode.offset + location.previousNode.length));
136+
let scanner = createScanner(document.getText(), true);
137+
scanner.setPosition(offset);
138+
scanner.scan();
139+
let isLast = scanner.getToken() === SyntaxKind.CloseBraceToken || scanner.getToken() === SyntaxKind.EOF;
140+
collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector);
141+
} else {
142+
if (location.path.length === 0) {
143+
collectPromise = this.jsonContribution.collectDefaultSuggestions(fileName, collector);
144+
} else {
145+
collectPromise = this.jsonContribution.collectValueSuggestions(fileName, location, collector);
146+
}
147+
}
148+
if (collectPromise) {
149+
return collectPromise.then(() => {
150+
if (items.length > 0) {
151+
return new CompletionList(items, isIncomplete);
152+
}
153+
return null;
154+
});
155+
}
156+
return null;
157+
}
158+
159+
private getCurrentWord(document: TextDocument, position: Position) {
160+
var i = position.character - 1;
161+
var text = document.lineAt(position.line).text;
162+
while (i >= 0 && ' \t\n\r\v":{[,'.indexOf(text.charAt(i)) === -1) {
163+
i--;
164+
}
165+
return text.substring(i+1, position.character);
166+
}
167+
}

0 commit comments

Comments
 (0)