Skip to content

Commit

Permalink
Merge pull request #281 from redhat-developer/workspace-folders
Browse files Browse the repository at this point in the history
Handle workspace folders better
  • Loading branch information
JPinkney authored Jul 17, 2020
2 parents 0dd40e4 + 78c552c commit 7346049
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 7 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ yaml.schemas: {
}
```

## Multi root schema association:
You can also use relative paths when working with multi root workspaces.

Suppose you have a multi root workspace that is laid out like:
```
My_first_project:
test.yaml
my_schema.json
My_second_project:
test2.yaml
my_schema2.json
```

You must then associate schemas relative to the root of the multi root workspace project.
```
yaml.schemas: {
"My_first_project/my_schema.json": "test.yaml",
"My_second_project/my_schema2.json": "test2.yaml"
}
```

`yaml.schemas` allows you to specify json schemas that you want to validate against the yaml that you write. Kubernetes is an optional field. It does not require a url as the language server will provide that. You just need the keyword kubernetes and a glob pattern.

## Clients
Expand Down
20 changes: 17 additions & 3 deletions src/languageservice/utils/paths.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WorkspaceFolder } from 'vscode-languageserver';
import { join, normalize } from 'path';
import { WorkspaceFolder, WorkspaceFoldersChangeEvent } from 'vscode-languageserver';
import { join, normalize, sep } from 'path';
import { URI } from 'vscode-uri';

export const isRelativePath = (path: string): boolean => {
Expand All @@ -14,7 +14,7 @@ export const relativeToAbsolutePath = (workspaceFolders: WorkspaceFolder[], work
// Convert it into an absolute path with the appropriate root folder path
if (uri.startsWith(folder.name)) {
const pathToFolder = URI.parse(folder.uri).fsPath;
const withoutFolderPrefix = uri.split(folder.name);
const withoutFolderPrefix = uri.split(sep);
withoutFolderPrefix.shift();

return URI.file(join(pathToFolder, withoutFolderPrefix.join())).toString();
Expand All @@ -30,3 +30,17 @@ export const relativeToAbsolutePath = (workspaceFolders: WorkspaceFolder[], work
// Fallback in case nothing could be applied
return normalize(uri);
};

export const workspaceFoldersChanged = (workspaceFolders: WorkspaceFolder[], changedFolders: WorkspaceFoldersChangeEvent): WorkspaceFolder[] => {
workspaceFolders = workspaceFolders.filter(e => {
return !changedFolders.removed.some(f => {
return f.uri === e.uri;
});
});
workspaceFolders = workspaceFolders.filter(e => {
return !changedFolders.added.some(f => {
return f.uri === e.uri;
});
}).concat(changedFolders.added);
return workspaceFolders;
};
21 changes: 18 additions & 3 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CustomSchemaProvider, FilePatternAssociation, SchemaDeletions, SchemaAd
import { JSONSchema } from './languageservice/jsonSchema';
import { SchemaAssociationNotification, DynamicCustomSchemaRequestRegistration, CustomSchemaRequest, SchemaModificationNotification } from './requestTypes';
import { schemaRequestHandler } from './languageservice/services/schemaRequestHandler';
import { isRelativePath, relativeToAbsolutePath } from './languageservice/utils/paths';
import { isRelativePath, relativeToAbsolutePath, workspaceFoldersChanged } from './languageservice/utils/paths';
import { URI } from 'vscode-uri';
import { KUBERNETES_SCHEMA_URL, JSON_SCHEMASTORE_URL } from './languageservice/utils/schemaUrls';
// tslint:disable-next-line: no-any
Expand Down Expand Up @@ -105,6 +105,7 @@ let workspaceFolders: WorkspaceFolder[] = [];
let clientDynamicRegisterSupport = false;
let hierarchicalDocumentSymbolSupport = false;
let clientDefinitionLinkSupport = false;
let hasWorkspaceFolderCapability = false;

/****************************
* Reusable helper functions
Expand Down Expand Up @@ -359,7 +360,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
capabilities.textDocument.definition &&
capabilities.textDocument.definition.linkSupport
);

hasWorkspaceFolderCapability = capabilities.workspace && !!capabilities.workspace.workspaceFolders;
return {
capabilities: {
textDocumentSync: documents.syncKind,
Expand All @@ -368,11 +369,25 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
documentSymbolProvider: true,
documentFormattingProvider: false,
documentRangeFormattingProvider: false,
definitionProvider: true
definitionProvider: true,
workspace: {
workspaceFolders: {
changeNotifications: true,
supported: true
}
}
}
};
});

connection.onInitialized(() => {
if (hasWorkspaceFolderCapability) {
connection.workspace.onDidChangeWorkspaceFolders(changedFolders => {
workspaceFolders = workspaceFoldersChanged(workspaceFolders, changedFolders);
});
}
});

/**
* Received a notification from the client with schema associations from other extensions
* Update the associations in the server
Expand Down
83 changes: 82 additions & 1 deletion test/paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as assert from 'assert';
import { WorkspaceFolder } from 'vscode-languageserver';
import { join } from 'path';

import { relativeToAbsolutePath, isRelativePath } from '../src/languageservice/utils/paths';
import { relativeToAbsolutePath, isRelativePath, workspaceFoldersChanged } from '../src/languageservice/utils/paths';
import { URI } from 'vscode-uri';

class TestWorkspace {
Expand Down Expand Up @@ -55,6 +55,18 @@ const ws3 = new TestWorkspace([
],
'file:///c%3A/Users/testuser/dev/potatoes');

const ws4 = new TestWorkspace([
{
uri: 'file:///c%3A/Users/testuser/dev/test',
name: 'test'
},
{
uri: 'file:///c%3A/Users/testuser/dev/test2',
name: 'test2'
}
],
'file:///c%3A/Users/testuser/dev/test2');

const checkBadPath = (path: string): void => {
it('Rejects "' + path + '"', () => {
assert(!isRelativePath(path));
Expand Down Expand Up @@ -141,6 +153,7 @@ suite('File path tests', () => {
const path1 = join('aFolder', 'file.json');
const path2 = join('folder-2', 'file.json');
const path3 = join('carrots', 'file.json');
const path4 = join('test', 'test.json');

it('Recognises relative path "' + path1 + '"', () => {
assert(isRelativePath(path1));
Expand All @@ -157,6 +170,10 @@ suite('File path tests', () => {
it('Resolves "' + path3 + '" in multi-root nested workspace', () => {
assert.equal(ws3.resolve(path3), 'file:///c%3A/Users/testuser/dev/carrots/file.json');
});

it('Resolves "' + path4 + '" in multi-root nested workspace', () => {
assert.equal(ws4.resolve(path4), 'file:///c%3A/Users/testuser/dev/test/test.json');
});
});

describe('Path with mixed delimiters (Windows only)', () => {
Expand Down Expand Up @@ -194,5 +211,69 @@ suite('File path tests', () => {
}
});
});

describe('Tests for workspaceFoldersChanged', () => {
it('workspaceFolders are added correctly', () => {
const newWorkspaceFolders = workspaceFoldersChanged(ws2.folders, {
added: [{
name: 'folder-4',
uri: 'file:///usr/testuser/projects/workspace/folder-4/'
}],
removed: []
});
assert.equal(newWorkspaceFolders.length, 4);
assert.equal(newWorkspaceFolders[0].name, 'folder-1');
assert.equal(newWorkspaceFolders[0].uri, 'file:///usr/testuser/projects/workspace/folder-1/');
assert.equal(newWorkspaceFolders[1].name, 'folder-2');
assert.equal(newWorkspaceFolders[1].uri, 'file:///usr/testuser/projects/workspace/folder-2/');
assert.equal(newWorkspaceFolders[2].name, 'folder-3');
assert.equal(newWorkspaceFolders[2].uri, 'file:///usr/testuser/projects/workspace/folder-3/');
assert.equal(newWorkspaceFolders[3].name, 'folder-4');
assert.equal(newWorkspaceFolders[3].uri, 'file:///usr/testuser/projects/workspace/folder-4/');
});
it('workspaceFolders are not added if duplicate uri', () => {
const newWorkspaceFolders = workspaceFoldersChanged(ws2.folders, {
added: [{
name: 'folder-3',
uri: 'file:///usr/testuser/projects/workspace/folder-3/'
}],
removed: []
});
assert.equal(newWorkspaceFolders.length, 3);
assert.equal(newWorkspaceFolders[0].name, 'folder-1');
assert.equal(newWorkspaceFolders[0].uri, 'file:///usr/testuser/projects/workspace/folder-1/');
assert.equal(newWorkspaceFolders[1].name, 'folder-2');
assert.equal(newWorkspaceFolders[1].uri, 'file:///usr/testuser/projects/workspace/folder-2/');
assert.equal(newWorkspaceFolders[2].name, 'folder-3');
assert.equal(newWorkspaceFolders[2].uri, 'file:///usr/testuser/projects/workspace/folder-3/');
});
it('workspaceFolders are removed correctly', () => {
const newWorkspaceFolders = workspaceFoldersChanged(ws2.folders, {
added: [],
removed: [{
name: 'folder-3',
uri: 'file:///usr/testuser/projects/workspace/folder-3/'
}]
});
assert.equal(newWorkspaceFolders.length, 2);
assert.equal(newWorkspaceFolders[0].name, 'folder-1');
assert.equal(newWorkspaceFolders[0].uri, 'file:///usr/testuser/projects/workspace/folder-1/');
assert.equal(newWorkspaceFolders[1].name, 'folder-2');
assert.equal(newWorkspaceFolders[1].uri, 'file:///usr/testuser/projects/workspace/folder-2/');
});
it('workspaceFolders empty event does nothing', () => {
const newWorkspaceFolders = workspaceFoldersChanged(ws2.folders, {
added: [],
removed: []
});
assert.equal(newWorkspaceFolders.length, 3);
assert.equal(newWorkspaceFolders[0].name, 'folder-1');
assert.equal(newWorkspaceFolders[0].uri, 'file:///usr/testuser/projects/workspace/folder-1/');
assert.equal(newWorkspaceFolders[1].name, 'folder-2');
assert.equal(newWorkspaceFolders[1].uri, 'file:///usr/testuser/projects/workspace/folder-2/');
assert.equal(newWorkspaceFolders[2].name, 'folder-3');
assert.equal(newWorkspaceFolders[2].uri, 'file:///usr/testuser/projects/workspace/folder-3/');
});
});
});
});

0 comments on commit 7346049

Please sign in to comment.