Skip to content

Commit

Permalink
Folding ranges (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
wkillerud authored Nov 30, 2024
1 parent dda764e commit ae6b6b5
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 3 deletions.
39 changes: 39 additions & 0 deletions extension/test/electron/definition/folding-ranges.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const assert = require('node:assert');
const path = require('node:path');
const vscode = require('vscode');
const { showFile, sleepCI } = require('../util');

const stylesUri = vscode.Uri.file(
path.resolve(__dirname, 'fixtures', 'styles.scss')
);

before(async () => {
await showFile(stylesUri);
await sleepCI();
});

after(async () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});

/**
* @param {import('vscode').Uri} documentUri
* @param {Array<import('vscode').Position>} positions
* @returns {Promise<Array<import('vscode').FoldingRange>>}
*/
async function getFoldingRanges(documentUri, positions) {
const result = await vscode.commands.executeCommand(
'vscode.executeFoldingRangeProvider',
documentUri,
positions
);
return result;
}

test('gets document folding ranges', async () => {
const [result] = await getFoldingRanges(stylesUri, [
new vscode.Position(7, 5),
]);

assert.ok(result, 'Should have gotten folding ranges');
});
21 changes: 21 additions & 0 deletions pkgs/sass_language_server/lib/src/language_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ class LanguageServer {
documentHighlightProvider: Either2.t1(true),
documentLinkProvider: DocumentLinkOptions(resolveProvider: false),
documentSymbolProvider: Either2.t1(true),
foldingRangeProvider: Either3.t1(true),
referencesProvider: Either2.t1(true),
renameProvider: Either2.t2(RenameOptions(prepareProvider: true)),
selectionRangeProvider: Either3.t1(true),
Expand Down Expand Up @@ -433,6 +434,26 @@ class LanguageServer {
}
});

_connection.onFoldingRanges((params) async {
try {
var document = _documents.get(params.textDocument.uri);
if (document == null) {
return [];
}

var configuration = _getLanguageConfiguration(document);
if (configuration.foldingRanges.enabled) {
var result = _ls.getFoldingRanges(document);
return result;
} else {
return [];
}
} on Exception catch (e) {
_log.debug(e.toString());
return [];
}
});

_connection.onSelectionRanges((params) async {
try {
var document = _documents.get(params.textDocument.uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ class FeatureConfiguration {

class LanguageConfiguration {
late final FeatureConfiguration definition;
late final FeatureConfiguration highlights;
late final FeatureConfiguration documentSymbols;
late final FeatureConfiguration documentLinks;
late final FeatureConfiguration foldingRanges;
late final FeatureConfiguration highlights;
late final FeatureConfiguration references;
late final FeatureConfiguration rename;
late final FeatureConfiguration selectionRanges;
Expand All @@ -21,6 +22,8 @@ class LanguageConfiguration {
enabled: config?['documentSymbols']?['enabled'] as bool? ?? true);
documentLinks = FeatureConfiguration(
enabled: config?['documentLinks']?['enabled'] as bool? ?? true);
foldingRanges = FeatureConfiguration(
enabled: config?['foldingRanges']?['enabled'] as bool? ?? true);
highlights = FeatureConfiguration(
enabled: config?['highlights']?['enabled'] as bool? ?? true);
references = FeatureConfiguration(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:lsp_server/lsp_server.dart' as lsp;
import 'package:sass_language_services/sass_language_services.dart';

import '../go_to_definition/scope.dart';
import '../go_to_definition/scope_visitor.dart';
import '../go_to_definition/scoped_symbols.dart';
import '../language_feature.dart';

class FoldingRangesFeature extends LanguageFeature {
FoldingRangesFeature({required super.ls});

List<lsp.FoldingRange> getFoldingRanges(TextDocument document) {
var stylesheet = ls.parseStylesheet(document);

var symbols = ls.cache.getDocumentSymbols(document) ??
ScopedSymbols(
stylesheet,
document.languageId == 'sass' ? Dialect.indented : Dialect.scss,
);
ls.cache.setDocumentSymbols(document, symbols);

var result = <lsp.FoldingRange>[];
// Omit the global scope.
for (var childScope in symbols.globalScope.children) {
result.addAll(_toFoldingRanges(document, childScope));
}
return result;
}

List<lsp.FoldingRange> _toFoldingRanges(TextDocument document, Scope scope) {
var result = <lsp.FoldingRange>[];
result.add(_toFoldingRange(document, scope));
if (scope.children.isEmpty) {
return result;
}
for (var childScope in scope.children) {
result.addAll(_toFoldingRanges(document, childScope));
}
return result;
}

lsp.FoldingRange _toFoldingRange(TextDocument document, Scope scope) {
var startLine = document.positionAt(scope.offset).line;
var endLine = document.positionAt(scope.offset + scope.length).line;
return lsp.FoldingRange(startLine: startLine, endLine: endLine);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
return null;
}

@override
void visitAtRule(sass.AtRule node) {
if (node.children != null) {
var span = node.span;
_addScope(
offset: span.start.offset,
length: span.length,
);
}
super.visitAtRule(node);
}

@override
void visitAtRootRule(sass.AtRootRule node) {
var span = node.span;
_addScope(
offset: span.start.offset,
length: span.length,
);
super.visitAtRootRule(node);
}

@override
void visitDeclaration(node) {
var isCustomProperty =
Expand Down Expand Up @@ -241,6 +263,21 @@ class ScopeVisitor with sass.RecursiveStatementVisitor {
super.visitIfRule(node);
}

@override
void visitIncludeRule(sass.IncludeRule node) {
var span = node.span;

var argsEndIndex = node.arguments.span.end.offset - span.start.offset;
var scopeIndex = span.text.indexOf(openBracketOrNewline, argsEndIndex);

_addScope(
offset: span.start.offset + scopeIndex,
length: span.length - scopeIndex,
);

super.visitIncludeRule(node);
}

@override
void visitMixinRule(node) {
var span = node.span;
Expand Down
11 changes: 9 additions & 2 deletions pkgs/sass_language_services/lib/src/language_services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:sass_api/sass_api.dart' as sass;
import 'package:sass_language_services/sass_language_services.dart';
import 'package:sass_language_services/src/features/document_highlights/document_highlights_feature.dart';
import 'package:sass_language_services/src/features/find_references/find_references_feature.dart';
import 'package:sass_language_services/src/features/folding_ranges/folding_ranges_feature.dart';
import 'package:sass_language_services/src/features/go_to_definition/go_to_definition_feature.dart';
import 'package:sass_language_services/src/features/rename/rename_feature.dart';
import 'package:sass_language_services/src/features/selection_ranges/selection_ranges_feature.dart';
Expand All @@ -23,8 +24,9 @@ class LanguageServices {
late final DocumentHighlightsFeature _documentHighlights;
late final DocumentLinksFeature _documentLinks;
late final DocumentSymbolsFeature _documentSymbols;
late final GoToDefinitionFeature _goToDefinition;
late final FoldingRangesFeature _foldingRanges;
late final FindReferencesFeature _findReferences;
late final GoToDefinitionFeature _goToDefinition;
late final RenameFeature _rename;
late final SelectionRangesFeature _selectionRanges;
late final WorkspaceSymbolsFeature _workspaceSymbols;
Expand All @@ -36,8 +38,9 @@ class LanguageServices {
_documentHighlights = DocumentHighlightsFeature(ls: this);
_documentLinks = DocumentLinksFeature(ls: this);
_documentSymbols = DocumentSymbolsFeature(ls: this);
_goToDefinition = GoToDefinitionFeature(ls: this);
_findReferences = FindReferencesFeature(ls: this);
_foldingRanges = FoldingRangesFeature(ls: this);
_goToDefinition = GoToDefinitionFeature(ls: this);
_rename = RenameFeature(ls: this);
_selectionRanges = SelectionRangesFeature(ls: this);
_workspaceSymbols = WorkspaceSymbolsFeature(ls: this);
Expand Down Expand Up @@ -70,6 +73,10 @@ class LanguageServices {
return _workspaceSymbols.findWorkspaceSymbols(query);
}

List<lsp.FoldingRange> getFoldingRanges(TextDocument document) {
return _foldingRanges.getFoldingRanges(document);
}

List<lsp.SelectionRange> getSelectionRanges(
TextDocument document, List<lsp.Position> positions) {
return _selectionRanges.getSelectionRanges(document, positions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import 'package:lsp_server/lsp_server.dart' as lsp;
import 'package:sass_language_services/sass_language_services.dart';
import 'package:test/test.dart';

import '../../memory_file_system.dart';
import '../../test_client_capabilities.dart';

final fs = MemoryFileSystem();
final ls = LanguageServices(fs: fs, clientCapabilities: getCapabilities());

lsp.FoldingRange fr(int startLine, int endLine) {
return lsp.FoldingRange(
startLine: startLine,
endLine: endLine,
);
}

void main() {
group('folding ranges', () {
setUp(() {
ls.cache.clear();
});

test('style rules', () {
var document = fs.createDocument('''
.foo {
color: red;
.bar {
color: blue;
}
}
''');

var result = ls.getFoldingRanges(document);
expect(result, hasLength(2));
expect(
result,
equals([
fr(0, 6),
fr(3, 5),
]),
);
});

test('mixin rules', () {
var document = fs.createDocument('''@mixin foo {
color: red;
.bar {
color: blue;
}
}
''');

var result = ls.getFoldingRanges(document);
expect(result, hasLength(2));
expect(
result,
equals([
fr(0, 6),
fr(3, 5),
]),
);
});

test('include rules', () {
var document = fs.createDocument('''@include foo {
--color-foo: red;
}
''');

var result = ls.getFoldingRanges(document);
expect(result, hasLength(1));
expect(
result,
equals([
fr(0, 2),
]),
);
});
});
}

0 comments on commit ae6b6b5

Please sign in to comment.