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

Add support for workspace folders #383

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
37f6517
Experiment with workspaceFolders
natebosch Jan 21, 2021
d4b7435
Start hacking together a test
natebosch Jan 24, 2021
1027818
Merge branch 'master' into workspace-folders
natebosch Jan 24, 2021
ea701cd
Make directory URIs end in /, fix test for initialization
natebosch Jan 24, 2021
a6efb87
More tests passing
natebosch Jan 24, 2021
a56c349
Get existing tests passing
natebosch Jan 24, 2021
2a9eadd
Drop some echos, implement byMarker for real
natebosch Jan 24, 2021
b5e36b9
Try to handle a callback that throws, seems to not be working
natebosch Jan 24, 2021
8587c83
Fix the test
natebosch Jan 24, 2021
14e7f71
Test cleanup - dup and consistent naming
natebosch Jan 24, 2021
917ccab
Only set workspaceFolders on initialization if it will be supported
natebosch Jan 24, 2021
e612766
Potentially friendlier name
natebosch Jan 24, 2021
a1fe031
Handle failure on later calls
natebosch Jan 25, 2021
3de130d
Merge branch 'master' into workspace-folders
natebosch Jan 28, 2021
7b877e1
Add an error message, remove some prints
natebosch Jan 30, 2021
227a22c
Merge branch 'master' into workspace-folders
natebosch Feb 5, 2021
c9eb6a5
Consistently use trailing /
natebosch Feb 8, 2021
4122cd3
Another trailing slash
natebosch Feb 28, 2021
bcef1fd
Trailing slashes in test expectations
natebosch Feb 28, 2021
b072c6a
Merge branch 'master' into workspace-folders
natebosch Mar 10, 2021
0d4dae2
Merge branch 'master' into workspace-folders
natebosch Mar 15, 2021
fbcce1c
Drop trailing slashes
natebosch Mar 16, 2021
fcc8822
Merge branch 'master' into workspace-folders
natebosch Mar 20, 2021
5fa9822
Merge branch 'master' into workspace-folders
natebosch Mar 23, 2021
c67ff23
Merge branch 'master' into workspace-folders
natebosch Apr 21, 2022
235f569
Merge branch 'master' into workspace-folders
natebosch Apr 26, 2022
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
Prev Previous commit
Next Next commit
Start hacking together a test
- Add a stub for `lsc#workspace#byMarker()` to make it easier to write
  the tests.
- Read client capabilities in `StubServer`.
- Outline some tests. Add a test reading client capabilities.
  • Loading branch information
natebosch committed Jan 24, 2021
commit d4b7435d074eee6f2bf6bd8b3034999782cf2a30
9 changes: 9 additions & 0 deletions autoload/lsc/workspace.vim
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
function! lsc#workspace#byMarker() abort
" TODO
return function('<SID>Cheat')
endfunction

function! s:Cheat(file_path) abort
return fnamemodify(a:file_path, ':h')
endfunction

function! lsc#workspace#findMarker(file_path, markers) abort
for l:path in s:ParentDirectories(a:file_path)
if s:ContainsAny(l:path, a:markers) | return l:path | endif
Expand Down
5 changes: 4 additions & 1 deletion test/integration/lib/stub_lsp.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import 'package:json_rpc_2/json_rpc_2.dart';

class StubServer {
final Peer peer;
Future<Map<String, dynamic>> get initialization => _initialization.future;
final _initialization = Completer<Map<String, dynamic>>();

StubServer(this.peer, {Map<String, dynamic> capabilities = const {}}) {
peer
..registerMethod('initialize', (_) {
..registerMethod('initialize', (Parameters p) {
_initialization.complete(p.asMap.cast<String, dynamic>());
return {'capabilities': capabilities};
})
..registerMethod('initialized', (_) {
Expand Down
6 changes: 4 additions & 2 deletions test/integration/lib/test_bed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ class TestBed {
TestBed._(this.vim, this.clients);

static Future<TestBed> setup(
{Future<void> Function(Vim) beforeRegister, String config = ''}) async {
{Future<void> Function(Vim) beforeRegister,
String config = '',
String workingDirectory}) async {
final serverSocket = await ServerSocket.bind('localhost', 0);

final clients = serverSocket
.map((socket) => Peer(lspChannel(socket, socket),
onUnhandledError: (error, stack) =>
fail('Unhandled server error: $error')))
.asBroadcastStream();
final vim = await Vim.start();
final vim = await Vim.start(workingDirectory: workingDirectory);
await beforeRegister?.call(vim);
await vim.expr('RegisterLanguageServer("text", {'
'"command":"localhost:${serverSocket.port}",'
Expand Down
3 changes: 2 additions & 1 deletion test/integration/lib/vim_remote.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ class Vim {
final String name;
final Process _process;

static Future<Vim> start() async {
static Future<Vim> start({String workingDirectory}) async {
final version = (await Process.run('vim', ['--version'])).stdout as String;
assert(version.contains('+clientserver'));
final name = 'DARTVIM-${Random().nextInt(4294967296)}';
final process = await Process.start(
'vim', ['--servername', name, '-u', 'vimrc', '-U', 'NONE', '-V$name'],
workingDirectory: workingDirectory,
mode: ProcessStartMode.detachedWithStdio);
while (!await _isRunning(name)) {
await Future.delayed(const Duration(milliseconds: 100));
Expand Down
1 change: 1 addition & 0 deletions test/integration/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dependencies:
lsp: ^0.1.1
path: ^1.7.0
test: ^1.13.0
test_descriptor: ^1.2.0
170 changes: 170 additions & 0 deletions test/integration/test/workspace_folders_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import 'dart:async';
import 'dart:io';

import 'package:async/async.dart';
import 'package:_test/stub_lsp.dart';
import 'package:_test/test_bed.dart';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;

void main() {
group('WorkspaceRoot configured', () {
TestBed testBed;
Peer client;

setUpAll(() async {
await d.dir('workspaces', [
d.dir('foo', [
d.file('makefile'),
d.dir('lib', [d.file('foo.txt')])
]),
d.dir('bar', [
d.file('makefile'),
d.dir('lib', [d.file('bar.txt')])
])
]).create();
testBed = await TestBed.setup(
config: '"WorkspaceRoot": lsc#workspace#byMarker(),',
workingDirectory: d.sandbox,
);
});

setUp(() async {
final nextClient = testBed.clients.first;
await testBed.vim.edit('workspaces/lib/foo.txt');
await testBed.vim.sendKeys(':LSClientEnable<cr>');
client = await nextClient;
});

tearDown(() async {
await testBed.vim.sendKeys(':LSClientDisable<cr>');
await testBed.vim.sendKeys(':%bwipeout!<cr>');
await client.done;
client = null;
});

test('configuration honored in initialization', () async {
final server = StubServer(client);

await server.initialized;
final initialization = await server.initialization;
expect(initialization['capabilities']['workspace']['workspaceFolders'],
true);
expect(initialization['workspaceFolders'], [
{'uri': 'some uri', 'name': anything}
]);
});

test('does not send notificaiton without capability', () async {
final server = StubServer(client, capabilities: {
'workspace': {
'workspaceFolders': {
'supported': true,
'changeNotifications': false,
}
}
});

server.peer.registerMethod('workspace/didChangeWorkspaceFolders',
(Parameters p) {
fail('Unexpected call to didChangeWorkspaceFolders');
});
// TODO
});

test('Deregister test', () async {
// TODO, no support for this at all today
// final server = StubServer(client, capabilities: {
// 'workspace': {
// 'workspacefolders': {
// 'supported': true,
// 'changenotifications': 'something?',
// }
// }
// });
});

test('sends notifications with capability', () async {
final server = StubServer(client, capabilities: {
'workspace': {
'workspaceFolders': {
'supported': true,
'changeNotifications': true,
}
}
});

final changeController = StreamController<Map<String, dynamic>>();
final changeEvents = StreamQueue(changeController.stream);
server.peer.registerMethod('workspace/didChangeWorkspaceFolders',
(Parameters p) {
changeController.add(p['event'].asMap.cast<String, dynamic>());
});

await server.initialized;

await testBed.vim.edit('workspaces/bar/bar.txt');

final change = await changeEvents.next;
expect(change['removed'], isEmpty);
expect(change['added'], [
{'uri': 'some uri', 'name': anything}
]);

// TODO
});
});

group('WorkspaceRoot throws', () {
// TODO

test('can handle a WorkspaceRoot that throws', () async {
// TODO
});
});

group('No WorkspaceRoot configured', () {
TestBed testBed;
Peer client;

setUpAll(() async {
testBed = await TestBed.setup();
await d.dir('workspaces', [
d.dir('foo', [
d.file('makefile'),
d.dir('lib', [d.file('foo.txt')])
]),
d.dir('bar', [
d.file('makefile'),
d.dir('lib', [d.file('bar.txt')])
])
]).create();
});

setUp(() async {
final nextClient = testBed.clients.first;
await testBed.vim.edit('foo.txt');
await testBed.vim.sendKeys(':LSClientEnable<cr>');
client = await nextClient;
});

tearDown(() async {
await testBed.vim.sendKeys(':LSClientDisable<cr>');
await testBed.vim.sendKeys(':%bwipeout!<cr>');
final file = File('foo.txt');
if (await file.exists()) await file.delete();
await client.done;
client = null;
});

test('does not advertise capability', () async {
final server = StubServer(client);

await server.initialized;
final initialization = await server.initialization;
expect(initialization['capabilities']['workspace']['workspaceFolders'],
false);
});
});
}