Skip to content

Commit

Permalink
[A] region and tag support #43 #41
Browse files Browse the repository at this point in the history
- region and tag symbols
- syntax highlight for region and tag symbols
- foldingRange for region symbols
  • Loading branch information
Gerrnperl committed Oct 13, 2023
1 parent 3f3f66e commit 2c2c7e4
Show file tree
Hide file tree
Showing 9 changed files with 503 additions and 14 deletions.
42 changes: 40 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,25 @@
"struct": "var(--vscode-symbolIcon-structForeground)",
"event": "var(--vscode-symbolIcon-eventForeground)",
"operator": "var(--vscode-symbolIcon-operatorForeground)",
"typeparameter": "var(--vscode-symbolIcon-typeParameterForeground)"
"typeparameter": "var(--vscode-symbolIcon-typeParameterForeground)",
"__om_Tag__": "var(--vscode-symbolIcon-packageForeground)",
"__om_Region__": "var(--vscode-symbolIcon-packageForeground)"
},
"description": "override colors of specific symbols and some ui components.",
"properties": {
"__om_Tag__": {
"type": "string",
"title": "Tag",
"description": "The color of the custom tag symbols in the outline."
},
"__om_Region__": {
"type": "string",
"title": "Region",
"description": "The color of the custom region symbols in the outline."
},
"visibleRange": {
"type": "string",
"title": "Visible Range",
"description": "The color of the visible range in the outline."
},
"focusingItem": {
Expand Down Expand Up @@ -190,6 +203,31 @@
}
}
},
"outline-map.region.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable custom region and tag support. By default, regions start with the tag `#region <name>`and end with the tag `#endregion <name>`. This extension does not do semantic analysis for specific languages, and content that is not part of the comments section may also be recognized as a tag"
},
"outline-map.region.highlight": {
"type": "boolean",
"default": true,
"markdownDescription": "Enable semantic highlighting for custom regions and tags in the editor."
},
"outline-map.region.startRegion": {
"type": "string",
"default": "#region",
"description": "The start region tag. The word after the tag will be seen as the region name."
},
"outline-map.region.endRegion": {
"type": "string",
"default": "#endregion",
"description": "The end region tag. The word after the tag will be seen as the region name to close the region. If no name is specified, the last opened region will be closed."
},
"outline-map.region.tag": {
"type": "string",
"default": "#tag",
"description": "The start tag. The word after the tag will be seen as the tag name."
},
"outline-map.follow": {
"type": "string",
"enum": [
Expand Down Expand Up @@ -300,7 +338,7 @@
"category": "outline-map"
}
],
"keybindings":[
"keybindings": [
{
"command": "outline-map.toggleSearch",
"key": "Alt+l"
Expand Down
55 changes: 55 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,34 @@ export class SymbolNode {
};
this.children = [];
this.selector = ['#outline-root'];
this.mapCustomSymbol();
}


/**
* Create custom symbol kinds.
*
* The custom symbol kinds are used to represent the regions and tags in the document.
* The kind information is stored in the detail of the DocumentSymbol by the RegionSymbolProvider.
*
* This function should be called after the DocumentSymbol is mapped to a SymbolNode.
*/
mapCustomSymbol() {
// RegionSymbolProvider use 'Number' as a placeholder for regions and tags.
if (this.kind !== 'Number') {
return;
}
if (this.detail.startsWith('__om_Region__')) {
this.kind = '__om_Region__';
this.detail = this.detail.slice(13);
}
else if (this.detail.startsWith('__om_Tag__')) {
this.kind = '__om_Tag__';
this.detail = this.detail.slice(10);
}
}


/**
* Creates a tree of SymbolNodes from a list of DocumentSymbols.
* @param docSymbols The list of DocumentSymbols to create the tree from.
Expand All @@ -59,9 +85,38 @@ export class SymbolNode {
const root: SymbolNode[] = [];
const hiddenItem = config.hiddenItem();

// A known issue of vscode is that the DocumentSymbols returned by
// command 'vscode.executeDocumentSymbolProvider' does not include the
// information of the provider of the symbols.
//
// When a document has multiple symbol providers,
// the symbols from different providers are not hierarchical.
//
// This function tries to reconstruct the tree.
// It will move some siblings to be children of a node
// if the range of the node contains the range of the siblings
/**
* Reconstruct the tree of DocumentSymbols.
* @param docSymbols The list of DocumentSymbols to reconstruct, should be sorted by start line.
*/
function reconstructTree(docSymbols: DocumentSymbol[]) {
for (let i = 0; i < docSymbols.length; i++) {
const docSymbol = docSymbols[i];
for (let j = i + 1; j < docSymbols.length; j++) {
const sibling = docSymbols[j];
if (docSymbol.range.contains(sibling.range)) {
docSymbol.children.push(sibling);
docSymbols.splice(j, 1);
j--;
}
}
}
}

// Inner function to recursively transform the DocumentSymbols into SymbolNodes
function recursiveTransform(docSymbols: DocumentSymbol[], parent: SymbolNode | SymbolNode[]) {
docSymbols.sort((a, b) => a.range.start.line - b.range.start.line);
reconstructTree(docSymbols);
docSymbols.forEach((docSymbol) => {
const node = new SymbolNode(docSymbol);
if (hiddenItem.includes(node.kind.toLowerCase())) {
Expand Down
5 changes: 5 additions & 0 deletions src/extension/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ export const config = {
customFont: () => getConfig('customFont') as string,
customCSS: () => getConfig('customCSS') as string,
debug: () => getConfig('debug') as boolean,
regionEnabled: () => getConfig('enabled', 'outline-map.region') as boolean,
regionHighlight: () => getConfig('highlight', 'outline-map.region') as boolean,
regionStart: () => getConfig('startRegion', 'outline-map.region') as string,
regionEnd: () => getConfig('endRegion', 'outline-map.region') as string,
tag: () => getConfig('tag', 'outline-map.region') as string,
};
11 changes: 11 additions & 0 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { commandList } from './commands';
import { ScrollMsg } from '../common';
import { config } from './config';
import { debounce, throttle } from '../utils';
import { RegionProvider, tokensLegend } from './region';

// called when extension is activated
// extension is activated the very first time the command is executed
Expand Down Expand Up @@ -77,6 +78,16 @@ export function activate(context: ExtensionContext) {

...commandList.map(command => commands.registerCommand(command.name, command.fn.bind(null, outlineView))),
);

if (config.regionEnabled()) {
const regionSymbolProvider = new RegionProvider();
languages.registerDocumentSymbolProvider({ scheme: 'file' }, regionSymbolProvider);
languages.registerFoldingRangeProvider({ scheme: 'file' }, regionSymbolProvider);
if (config.regionHighlight()) {
languages.registerDocumentSemanticTokensProvider({ scheme: 'file' }, regionSymbolProvider, tokensLegend);
}
}

}

// called when extension is deactivated
Expand Down
2 changes: 1 addition & 1 deletion src/extension/outline.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { config } from './config';
import { commands, DocumentSymbol, ExtensionContext, SymbolKind, TextDocument, Uri, WebviewView, WebviewViewProvider, window, Range, Selection, Position, Diagnostic, DiagnosticSeverity } from 'vscode';
import { commands, DocumentSymbol, ExtensionContext, TextDocument, Uri, WebviewView, WebviewViewProvider, window, Range, Selection, Position, Diagnostic, DiagnosticSeverity } from 'vscode';
import { Msg, UpdateMsg, Op, UpdateOp, DeleteOp, InsertOp, SymbolNode, MoveOp, PinStatus } from '../common';


Expand Down
Loading

0 comments on commit 2c2c7e4

Please sign in to comment.