44 *--------------------------------------------------------------------------------------------*/
55
66import { DocumentLink } from 'vscode-languageserver-types' ;
7- import { TextDocument , ASTNode , PropertyASTNode , Range , Thenable } from '../jsonLanguageTypes' ;
7+ import { TextDocument , ASTNode , PropertyASTNode , Range , Thenable , FileSystemProvider , FileType , FileStat } from '../jsonLanguageTypes' ;
88import { JSONDocument } from '../parser/jsonParser' ;
99import { IJSONSchemaService } from './jsonSchemaService' ;
10- import { URI } from 'vscode-uri' ;
11- import { existsSync as fileExistsSync } from 'fs' ;
12- import * as path from 'path' ;
10+ import { URI , Utils } from 'vscode-uri' ;
1311
1412export class JSONLinks {
1513 private schemaService : IJSONSchemaService ;
14+ private fileSystemProvider : FileSystemProvider | undefined ;
1615
17- constructor ( schemaService : IJSONSchemaService ) {
16+ constructor ( schemaService : IJSONSchemaService , fileSystemProvider ?: FileSystemProvider ) {
1817 this . schemaService = schemaService ;
18+ this . fileSystemProvider = fileSystemProvider ;
1919 }
2020
2121 public findLinks ( document : TextDocument , doc : JSONDocument ) : Thenable < DocumentLink [ ] > {
22- return findLinks ( document , doc , this . schemaService ) ;
22+ return findLinks ( document , doc , this . schemaService , this . fileSystemProvider ) ;
2323 }
2424}
2525
26- export function findLinks ( document : TextDocument , doc : JSONDocument , schemaService ?: IJSONSchemaService ) : Thenable < DocumentLink [ ] > {
26+ export function findLinks ( document : TextDocument , doc : JSONDocument , schemaService ?: IJSONSchemaService , fileSystemProvider ?: FileSystemProvider ) : Thenable < DocumentLink [ ] > {
2727 const promises : Thenable < DocumentLink [ ] > [ ] = [ ] ;
2828
2929 const refLinks : DocumentLink [ ] = [ ] ;
@@ -39,32 +39,43 @@ export function findLinks(document: TextDocument, doc: JSONDocument, schemaServi
3939 } ) ;
4040 }
4141 }
42- if ( node . type === "property" && node . valueNode ?. type === 'string' && schemaService ) {
42+ if ( node . type === "property" && node . valueNode ?. type === 'string' && schemaService && fileSystemProvider ) {
4343 const pathNode = node . valueNode ;
4444 const promise = schemaService . getSchemaForResource ( document . uri , doc ) . then ( ( schema ) => {
4545 const pathLinks : DocumentLink [ ] = [ ] ;
4646 if ( ! schema ) {
4747 return pathLinks ;
4848 }
49- doc . getMatchingSchemas ( schema . schema , pathNode . offset ) . forEach ( ( s ) => {
49+
50+ const matchingSchemas = doc . getMatchingSchemas ( schema . schema , pathNode . offset ) ;
51+
52+ let resolvedRef = '' ;
53+ for ( const s of matchingSchemas ) {
5054 if ( s . node !== pathNode || s . inverted || ! s . schema ) {
51- return ; // Not an _exact_ schema match.
55+ continue ; // Not an _exact_ schema match.
5256 }
5357 if ( s . schema . format !== 'uri-reference' ) {
54- return ; // Not a uri-ref.
58+ continue ; // Not a uri-ref.
5559 }
5660 const pathURI = resolveURIRef ( pathNode . value , document ) ;
57- if ( ! pathURI ) {
58- return ; // Unable to resolve ref.
61+ if ( pathURI . scheme === 'file' ) {
62+ resolvedRef = pathURI . toString ( ) ;
5963 }
60- if ( fileExistsSync ( pathURI . fsPath ) ) {
61- pathLinks . push ( {
62- target : pathURI . toString ( ) ,
63- range : createRange ( document , pathNode )
64- } ) ;
65- }
66- } ) ;
67- return pathLinks ;
64+ }
65+
66+ if ( resolvedRef ) {
67+ return fileSystemProvider . stat ( resolvedRef ) . then ( ( fs ) => {
68+ if ( fileExists ( fs ) ) {
69+ pathLinks . push ( {
70+ target : resolvedRef ,
71+ range : createRange ( document , pathNode )
72+ } ) ;
73+ }
74+ return pathLinks ;
75+ } ) ;
76+ } else {
77+ return pathLinks ;
78+ }
6879 } ) ;
6980 promises . push ( promise ) ;
7081 }
@@ -77,6 +88,13 @@ export function findLinks(document: TextDocument, doc: JSONDocument, schemaServi
7788 } ) ;
7889}
7990
91+ function fileExists ( stat : FileStat ) : boolean {
92+ if ( stat . type === FileType . Unknown && stat . size === - 1 ) {
93+ return false ;
94+ }
95+ return true ;
96+ }
97+
8098function createRange ( document : TextDocument , node : ASTNode ) : Range {
8199 return Range . create ( document . positionAt ( node . offset + 1 ) , document . positionAt ( node . offset + node . length - 1 ) ) ;
82100}
@@ -133,12 +151,10 @@ function unescape(str: string): string {
133151 return str . replace ( / ~ 1 / g, '/' ) . replace ( / ~ 0 / g, '~' ) ;
134152}
135153
136- function resolveURIRef ( ref : string , document : TextDocument ) : URI | null {
154+ function resolveURIRef ( ref : string , document : TextDocument ) : URI {
137155 if ( ref . indexOf ( '://' ) > 0 ) {
138156 // Already a fully qualified URI.
139- // The language service should already create a document link
140- // for these, so no need to created a duplicate.
141- return null ;
157+ return URI . parse ( ref ) ;
142158 }
143159
144160 if ( ref . startsWith ( '/' ) ) {
@@ -148,9 +164,5 @@ function resolveURIRef(ref: string, document: TextDocument): URI | null {
148164
149165 // Resolve ref relative to the document.
150166 const docURI = URI . parse ( document . uri ) ;
151- const docDir = path . dirname ( docURI . path ) ;
152- const refPath = path . join ( docDir , ref ) ;
153- return docURI . with ( {
154- path : refPath
155- } ) ;
167+ return Utils . joinPath ( Utils . dirname ( docURI ) , ref ) ;
156168}
0 commit comments