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

List remote files #92

Closed
wants to merge 3 commits into from
Closed
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
1 change: 0 additions & 1 deletion jest/onig.cjs

This file was deleted.

3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@
"configFile": "./babel.test.config.cjs"
}
]
},
"moduleNameMapper": {
"^vscode-oniguruma/release/onig.wasm$": "<rootDir>/jest/onig.js"
}
},
"overrides": {
Expand Down
36 changes: 35 additions & 1 deletion src/customRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,32 @@ export interface GetTextDocumentParams {

export interface GetTextDocumentResult {
text: string
mtime?: number
}

export const getTextDocumentRequestType = new ProtocolRequestType<GetTextDocumentParams, GetTextDocumentResult, never, void, void>('textDocument/get')

export interface StatFileParams {
uri: string
}
export interface StatFileResult {
type: 'directory' | 'file'
size: number
name: string
mtime: number
}

export const getFileStatsRequestType = new ProtocolRequestType<StatFileParams, StatFileResult, never, void, void>('file/stats')

export interface ListFilesParams {
directory: string
}
export interface ListFilesResult {
files: string[]
}

export const listFileRequestType = new ProtocolRequestType<ListFilesParams, ListFilesResult, never, void, void>('file/list')

export function updateFile (uri: string, text: string, languageClient: LanguageClient): Promise<void> {
return languageClient.sendRequest(saveTextDocumentRequestType, {
textDocument: {
Expand All @@ -36,10 +58,22 @@ export function updateFile (uri: string, text: string, languageClient: LanguageC
})
}

export function getFile (uri: string, languageClient: LanguageClient): Promise<{ text: string }> {
export function getFileContent (uri: string, languageClient: LanguageClient): Promise<GetTextDocumentResult> {
return languageClient.sendRequest(getTextDocumentRequestType, {
textDocument: {
uri
}
})
}

export function getFileStats (uri: string, languageClient: LanguageClient): Promise<StatFileResult> {
return languageClient.sendRequest(getFileStatsRequestType, {
uri
})
}

export function listFiles (directory: string, languageClient: LanguageClient): Promise<ListFilesResult> {
return languageClient.sendRequest(listFileRequestType, {
directory
})
}
54 changes: 35 additions & 19 deletions src/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,24 @@ class InfrastructureTextFileSystemProvider implements IFileSystemProviderWithFil
constructor (private infrastructure: Infrastructure, private languageClientManager: LanguageClientManager) {
}

private cachedContent: Map<string, Promise<string | undefined>> = new Map()
private async getFileContent (resource: monaco.Uri): Promise<string | undefined> {
private isBlacklisted (resource: monaco.Uri) {
const REMOTE_FILE_BLACKLIST = ['.git/config', '.vscode', monaco.Uri.parse(this.infrastructure.rootUri).path]

const blacklisted = REMOTE_FILE_BLACKLIST.some(blacklisted => resource.path.endsWith(blacklisted))
if (blacklisted) {
return undefined
}
if (!this.cachedContent.has(resource.toString())) {
this.cachedContent.set(resource.toString(), this.infrastructure.getFileContent!(resource, this.languageClientManager))
}
return await this.cachedContent.get(resource.toString())
return blacklisted
}

async readFile (resource: monaco.Uri): Promise<Uint8Array> {
const content = await this.getFileContent(resource)
return encoder.encode(content)
if (this.isBlacklisted(resource)) {
throw FileSystemProviderError.create('Not allowed', FileSystemProviderErrorCode.NoPermissions)
}
try {
const file = await this.infrastructure.getFileContent!(resource, this.languageClientManager)

return encoder.encode(file)
} catch (err) {
throw FileSystemProviderError.create(err as Error, FileSystemProviderErrorCode.Unknown)
}
}

async writeFile (): Promise<void> {
Expand All @@ -116,13 +117,16 @@ class InfrastructureTextFileSystemProvider implements IFileSystemProviderWithFil

async stat (resource: monaco.Uri): Promise<IStat> {
try {
const content = await this.getFileContent(resource)
if (content != null) {
if (this.isBlacklisted(resource)) {
throw FileSystemProviderError.create('Not allowed', FileSystemProviderErrorCode.NoPermissions)
}
const fileStats = await this.infrastructure.getFileStats?.(resource, this.languageClientManager)
if (fileStats != null) {
return {
type: FileType.File,
size: encoder.encode(content).length,
mtime: Date.now(),
ctime: Date.now()
type: fileStats.type === 'directory' ? FileType.Directory : FileType.File,
size: fileStats.size,
mtime: fileStats.mtime,
ctime: 0
}
}
} catch (err) {
Expand All @@ -134,8 +138,20 @@ class InfrastructureTextFileSystemProvider implements IFileSystemProviderWithFil
async mkdir (): Promise<void> {
}

async readdir () {
return []
async readdir (resource: monaco.Uri) {
const result = await this.infrastructure.listFiles?.(resource, this.languageClientManager)
if (result == null) {
return []
}
return result.map(file => {
let name = file
let type = FileType.File
if (file.endsWith('/')) {
type = FileType.Directory
name = file.slice(0, -1)
}
return <[string, FileType]>[name, type]
})
}

delete (): Promise<void> {
Expand Down
28 changes: 26 additions & 2 deletions src/infrastructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MessageTransports } from 'vscode-languageclient'
import * as monaco from 'monaco-editor'
import * as vscode from 'vscode'
import { LSPAny } from 'vscode-languageserver-protocol'
import { getFile, updateFile } from './customRequests'
import { StatFileResult, getFileContent, getFileStats, listFiles, updateFile } from './customRequests'
import { LanguageClientManager } from './languageClient'
import { LanguageClientId, LanguageClientOptions } from './languageClientOptions'

Expand Down Expand Up @@ -41,6 +41,18 @@ export interface Infrastructure {
* @param languageClient The languageclient we're trying to get the file from
*/
getFileContent? (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<string | undefined>
/**
* List the files of a directory
* @param resource the Uri of the directory
* @param languageClient The languageclient we're trying to get the file from
*/
getFileStats? (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<StatFileResult>
/**
* List the files of a directory
* @param resource the Uri of the directory
* @param languageClient The languageclient we're trying to get the file from
*/
listFiles? (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<string[]>

/**
* Open a connection to the language server
Expand Down Expand Up @@ -114,12 +126,24 @@ export abstract class CodinGameInfrastructure implements Infrastructure {

public async getFileContent (resource: monaco.Uri, languageClient: LanguageClientManager): Promise<string | undefined> {
try {
return (await getFile(resource.toString(true), languageClient)).text
return (await getFileContent(resource.toString(true), languageClient)).text
} catch (error) {
return undefined
}
}

public async getFileStats (directory: monaco.Uri, languageClient: LanguageClientManager): Promise<StatFileResult> {
return (await getFileStats(directory.toString(true), languageClient))
}

public async listFiles (directory: monaco.Uri, languageClient: LanguageClientManager): Promise<string[]> {
try {
return (await listFiles(directory.toString(true), languageClient)).files
} catch (error) {
return []
}
}

/**
* A function which returns a valid JWT token to use to connect to the server
*/
Expand Down
17 changes: 15 additions & 2 deletions src/tests/infrastructure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createModelReference } from 'vscode/monaco'
import * as vscode from 'vscode'
import { RegisteredFileSystemProvider, RegisteredMemoryFile, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override'
import pDefer, { TestInfrastructure, waitClientNotification, waitClientRequest } from './tools'
import { GetTextDocumentParams, getTextDocumentRequestType, GetTextDocumentResult, saveTextDocumentRequestType } from '../customRequests'
import { getFileStatsRequestType, GetTextDocumentParams, getTextDocumentRequestType, GetTextDocumentResult, saveTextDocumentRequestType, StatFileParams, StatFileResult } from '../customRequests'
import { createLanguageClientManager, LanguageClientManager, getLanguageClientOptions, StaticLanguageClientId } from '..'

async function initializeLanguageClientAndGetConnection (
Expand Down Expand Up @@ -182,7 +182,20 @@ async function testLanguageClient (
return editor
})

const [getDocumentRequest, sendGetDocumentRequestResponse] = await waitClientRequest<GetTextDocumentParams, GetTextDocumentResult, never>(handler => connection.onRequest(getTextDocumentRequestType, handler))
const getDocumentRequestPromise = waitClientRequest<GetTextDocumentParams, GetTextDocumentResult, never>(handler => connection.onRequest(getTextDocumentRequestType, handler))
const [getFileStatsRequest, sendGetFileStatsRequestResponse] = await waitClientRequest<StatFileParams, StatFileResult, never>(handler => connection.onRequest(getFileStatsRequestType, handler))
expect(getFileStatsRequest).toEqual({
uri: 'file:///tmp/project/src/main/Otherfile.java'
})

sendGetFileStatsRequestResponse({
mtime: 0,
name: 'Otherfile.java',
size: 50,
type: 'file'
})

const [getDocumentRequest, sendGetDocumentRequestResponse] = await getDocumentRequestPromise
expect(getDocumentRequest).toEqual({
textDocument: {
uri: 'file:///tmp/project/src/main/Otherfile.java'
Expand Down
16 changes: 14 additions & 2 deletions src/tests/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from 'vscode-languageserver/lib/common/api'
import { monaco } from '@codingame/monaco-editor-wrapper'
import { MessageTransports } from 'vscode-languageclient'
import { getFile, updateFile } from '../customRequests'
import { getFileContent, getFileStats, listFiles, StatFileResult, updateFile } from '../customRequests'
import { Infrastructure, LanguageClientId, LanguageClientManager, LanguageClientOptions } from '../'

class PipedMessageReader extends AbstractMessageReader {
Expand Down Expand Up @@ -133,12 +133,24 @@ export class TestInfrastructure implements Infrastructure {
// use same method as CodinGameInfrastructure to be able to simply catch it
async getFileContent (resource: Uri, languageClient: LanguageClientManager): Promise<string | undefined> {
try {
return (await getFile(resource.toString(true), languageClient)).text
return (await getFileContent(resource.toString(true), languageClient)).text
} catch (error) {
return undefined
}
}

public async getFileStats (directory: monaco.Uri, languageClient: LanguageClientManager): Promise<StatFileResult> {
return (await getFileStats(directory.toString(true), languageClient))
}

public async listFiles (directory: monaco.Uri, languageClient: LanguageClientManager): Promise<string[]> {
try {
return (await listFiles(directory.toString(true), languageClient)).files
} catch (error) {
return []
}
}

// use same method as CodinGameInfrastructure to be able to simply catch it
public async saveFileContent (document: monaco.Uri, content: string, languageClient: LanguageClientManager): Promise<void> {
if (languageClient.isConnected()) {
Expand Down
Loading