vscode#FileSearchProvider TypeScript Examples
The following examples show how to use
vscode#FileSearchProvider.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utopia-fs.ts From utopia with MIT License | 4 votes |
export class UtopiaFSExtension
implements FileSystemProvider, FileSearchProvider, TextSearchProvider, Disposable
{
private disposable: Disposable
private fileChangeEventQueue = newEventQueue<FileChangeEvent>()
private utopiaSavedChangeEventQueue = newEventQueue<Uri>()
private utopiaUnsavedChangeEventQueue = newEventQueue<Uri>()
private allFilePaths: string[] | null = null
constructor(private projectID: string) {
this.disposable = Disposable.from(
workspace.registerFileSystemProvider(projectID, this, { isCaseSensitive: true }),
workspace.registerFileSearchProvider(projectID, this),
workspace.registerTextSearchProvider(projectID, this),
)
}
dispose() {
this.disposable?.dispose()
}
// FileSystemProvider
readonly onDidChangeFile: Event<FileChangeEvent[]> = this.fileChangeEventQueue.emitter.event
readonly onUtopiaDidChangeSavedContent: Event<Uri[]> =
this.utopiaSavedChangeEventQueue.emitter.event
readonly onUtopiaDidChangeUnsavedContent: Event<Uri[]> =
this.utopiaUnsavedChangeEventQueue.emitter.event
private queueEvent<T>(event: T, eventQueue: EventQueue<T>): void {
eventQueue.queue.push(event)
if (eventQueue.handle != null) {
clearTimeout(eventQueue.handle)
}
eventQueue.handle = setTimeout(() => {
eventQueue.emitter.fire(eventQueue.queue)
eventQueue.queue = []
}, 5)
}
private queueFileChangeEvent(event: FileChangeEvent): void {
this.clearCachedFiles()
this.queueEvent(event, this.fileChangeEventQueue)
}
private queueUtopiaSavedChangeEvent(resource: Uri): void {
this.queueEvent(resource, this.utopiaSavedChangeEventQueue)
}
private queueUtopiaUnsavedChangeEvent(resource: Uri): void {
this.queueEvent(resource, this.utopiaUnsavedChangeEventQueue)
}
private async notifyFileChanged(path: string, modifiedBySelf: boolean): Promise<void> {
const uri = toUtopiaURI(this.projectID, path)
const hasUnsavedContent = await pathIsFileWithUnsavedContent(path)
const fileWasSaved = !hasUnsavedContent
if (fileWasSaved) {
// Notify VS Code of updates to the saved content
this.queueFileChangeEvent({
type: FileChangeType.Changed,
uri: uri,
})
}
if (!modifiedBySelf) {
// Notify our extension of changes coming from Utopia only
if (fileWasSaved) {
this.queueUtopiaSavedChangeEvent(uri)
} else {
this.queueUtopiaUnsavedChangeEvent(uri)
}
}
}
private notifyFileCreated(path: string): void {
this.queueFileChangeEvent({
type: FileChangeType.Created,
uri: toUtopiaURI(this.projectID, path),
})
}
private notifyFileDeleted(path: string): void {
this.queueFileChangeEvent({
type: FileChangeType.Deleted,
uri: toUtopiaURI(this.projectID, path),
})
}
watch(uri: Uri, options: { recursive: boolean; excludes: string[] }): Disposable {
const path = fromUtopiaURI(uri)
watch(
path,
options.recursive,
this.notifyFileCreated.bind(this),
this.notifyFileChanged.bind(this),
this.notifyFileDeleted.bind(this),
() => {
/* no op */
},
)
return new Disposable(() => {
stopWatching(path, options.recursive)
})
}
async exists(uri: Uri): Promise<boolean> {
const path = fromUtopiaURI(uri)
return exists(path)
}
async stat(uri: Uri): Promise<FileStat> {
const path = fromUtopiaURI(uri)
const stats = await stat(path)
const fileType = isDirectory(stats) ? FileType.Directory : FileType.File
return {
type: fileType,
ctime: stats.ctime.valueOf(),
mtime: stats.lastSavedTime.valueOf(), // VS Code is only interested in changes to the saved content
size: stats.size,
}
}
async readDirectory(uri: Uri): Promise<[string, FileType][]> {
const path = fromUtopiaURI(uri)
const children = await readDirectory(path)
const result: Promise<[string, FileType]>[] = children.map((childName) =>
pathIsDirectory(appendToPath(path, childName)).then((resultIsDirectory) => [
childName,
resultIsDirectory ? FileType.Directory : FileType.File,
]),
)
return Promise.all(result)
}
async createDirectory(uri: Uri): Promise<void> {
const path = fromUtopiaURI(uri)
await createDirectory(path)
}
async readFile(uri: Uri): Promise<Uint8Array> {
const path = fromUtopiaURI(uri)
return readFileSavedContent(path)
}
async writeFile(
uri: Uri,
content: Uint8Array,
options: { create: boolean; overwrite: boolean },
): Promise<void> {
const path = fromUtopiaURI(uri)
if (!options.create || !options.overwrite) {
const fileExists = await exists(path)
if (!fileExists && !options.create) {
throw FileSystemError.FileNotFound(uri)
} else if (fileExists && !options.overwrite) {
throw FileSystemError.FileExists(uri)
}
}
await writeFileSavedContent(path, content)
}
async delete(uri: Uri, options: { recursive: boolean }): Promise<void> {
const path = fromUtopiaURI(uri)
await deletePath(path, options.recursive)
}
async rename(oldUri: Uri, newUri: Uri, options: { overwrite: boolean }): Promise<void> {
const oldPath = fromUtopiaURI(oldUri)
const newPath = fromUtopiaURI(newUri)
if (!options.overwrite) {
const fileExists = await exists(newPath)
if (fileExists) {
throw FileSystemError.FileExists(newUri)
}
}
await rename(oldPath, newPath)
}
async copy(source: Uri, destination: Uri, options: { overwrite: boolean }): Promise<void> {
// It's not clear where this will ever be called from, but it seems to be from the side bar
// that isn't available in Utopia, so this implementation is "just in case"
const sourcePath = fromUtopiaURI(source)
const destinationPath = fromUtopiaURI(destination)
const destinationParentDir = dirname(destinationPath)
const destinationParentDirExists = await exists(destinationParentDir)
if (!destinationParentDirExists) {
throw FileSystemError.FileNotFound(toUtopiaURI(this.projectID, destinationParentDir))
}
if (!options.overwrite) {
const destinationExists = await exists(destinationPath)
if (destinationExists && !options.overwrite) {
throw FileSystemError.FileExists(destination)
}
}
const { content, unsavedContent } = await readFile(sourcePath)
await writeFile(destinationPath, content, unsavedContent)
}
// FileSearchProvider
async provideFileSearchResults(
query: FileSearchQuery,
options: FileSearchOptions,
_token: CancellationToken,
): Promise<Uri[]> {
// TODO Support all search options
const { result: foundPaths } = await this.filterFilePaths(query.pattern, options.maxResults)
return foundPaths.map((p) => toUtopiaURI(this.projectID, p))
}
// TextSearchProvider
async provideTextSearchResults(
query: TextSearchQuery,
options: TextSearchOptions,
progress: Progress<TextSearchResult>,
token: CancellationToken,
): Promise<TextSearchComplete> {
// This appears to only be callable from the side bar that isn't available in Utopia
// TODO Support all search options
const { result: filePaths, limitHit } = await this.filterFilePaths(options.includes[0])
if (filePaths.length > 0) {
for (const filePath of filePaths) {
if (token.isCancellationRequested) {
break
}
const content = await readFileSavedContentAsUTF8(filePath)
const lines = splitIntoLines(content)
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const index = line.indexOf(query.pattern)
if (index !== -1) {
progress.report({
uri: toUtopiaURI(this.projectID, filePath),
ranges: new Range(
new Position(i, index),
new Position(i, index + query.pattern.length),
),
preview: {
text: line,
matches: new Range(
new Position(0, index),
new Position(0, index + query.pattern.length),
),
},
})
}
}
}
}
return { limitHit: limitHit }
}
// Common
private async filterFilePaths(
query: string | undefined,
maxResults?: number,
): Promise<{ result: string[]; limitHit: boolean }> {
const filePaths = await this.getAllPaths()
let result: string[] = []
let limitHit = false
let remainingCount = maxResults == null ? Infinity : maxResults
const pattern = query ? new RegExp(convertSimple2RegExpPattern(query)) : null
for (const path of filePaths) {
if (remainingCount < 0) {
break
}
if (!pattern || pattern.exec(stripRootPrefix(path))) {
if (remainingCount === 0) {
// We've already found the max number of results, but we want to flag that there are more
limitHit = true
} else {
result.push(path)
}
remainingCount--
}
}
return {
result: result,
limitHit: limitHit,
}
}
async getAllPaths(): Promise<string[]> {
if (this.allFilePaths == null) {
const result = await getDescendentPaths(RootDir)
this.allFilePaths = result
return result
} else {
return this.allFilePaths
}
}
private clearCachedFiles() {
this.allFilePaths = null
}
}