Yuli Hua
Yuli Hua

Reputation: 721

How can I implement my own code outline layout in vscode?

I'm looking for an extension in Visual Studio Code (vscode) where I can define my custom code outline. Essentially, listing all my functions/definitions in a tree-like manner.

Let's say I'm using a simple language that looks as follows:

begin foo1 arriving procedure
  move into queue1
  print queue1
  send to foo2 
end

begin foo2 arriving procedure
  move into queue2
  print queue2
  send to foo3
end

I would like to know if there is an extension for vscode that let's me implement something like this:

like this

Would be nice if it was clickable. To navigate/go to definition, and possible expandable in case of more complex code.

What I've found so far.

  1. vscode code outline https://github.com/patrys/vscode-code-outline , I like this extension except it doesn't work for my language. Example image for a .js file

  2. Show Functions https://marketplace.visualstudio.com/items?itemName=qrti.funclist

  3. Sourcecookifier for notepad++ (Can do what I want but for notepad++ obviously)

I like the second extension (Show Functions) as it is easily customizable in the vscode/settings file. You can define your own regular expression from settings. However, it is not in a outline view fixed to the editor. Nor is it refreshing live.

I like the first extension too as it is in a tree view but I don't seem to know how and where to modify the settings in order to achieve my layout described.

If anyone could point me in right directions it would be very appreciated. I already tried a bit with the documentation of code outline extension but I don't think it is making any sense to me.

PS: First post on StackOverflow please let me know if there's something I should add/change.

Thanks in advance.

Upvotes: 37

Views: 14366

Answers (4)

DocumentSymbolProvider: you can get nested symbols with children

For example, if you have:

class Myclass {
  function myFunc() {
    return 1
  }
}

then you'd want a nested outline:

Myclass
  myFunc

And the way to achieve this is something like:

class MySymbolProvider implements vscode.DocumentSymbolProvider {
    public provideDocumentSymbols(document, token) {
        const toplevelSymbols = []
        const myclassSymbol = new vscode.DocumentSymbol(...)
        toplevelSymbols.push(myclassSymbol)
        const myFunc = new vscode.DocumentSymbol(...)
        myclassSymbol.children.push(myFunc)
        return toplevelSymbols
    }
}

What you do is return only toplevel symbols, and then add children to it. There seems to be no way to do so via the constructor, you just have to push. .children does get automatically initialized to [] for you.

Tested in VS Code 1.91.1.

Related:

TODO: grammatically trigger outline refresh only after save when a background on save compiler finishes:

DocumentSymbolProvider: for nested symbols you also need ranges

These allows vscode to decide which symbol the cursor is currently over in order to implement "Follow Cursor" on the Outline. make VS Code parse and display the structure of a new language to the outline region of VSC provides a good example of what the ranges should look like, notably:

  • both column and line are 0 based
  • line is the actual last line that contains something of the symbol
  • column appears to be 1 past the last 0-indexed column of the last character of the symbol
class MyClass {
  /**
   * Some method documentation
   */
  private function myFunction(param1) {
    console.log(param1);
  }
}

In this case, the ranges would be

rangeOfClass = {
  "start": { "line": 0, "character": 0 },
  "end": { "line": 7, "character": 1 }
};
rangeOfMethod = {
  "start": { "line": 4, "character": 2 },
  "end": { "line": 6, "character": 3 }
};

and the selectionRange could be only the name of the symbol

selectionRangeOfClass = {
  "start": { "line": 0, "character": 6 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 4, "character": 19 },
  "end": { "line": 4, "character": 29 }
};

or it could include from the documentation, keywords and modifiers and upto the end of the symbol

selectionRangeOfClass = {
  "start": { "line": 0, "character": 0 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 1, "character": 2 },
  "end": { "line": 4, "character": 29 }
};

Upvotes: 0

Jerry T
Jerry T

Reputation: 1690

Now we have https://marketplace.visualstudio.com/items?itemName=Gerrnperl.outline-map&ssr=false#overview

I like this better, because

  • its sidebar is on the right
  • Two way sync-scroll
  • Custom rules, obviously.

Upvotes: 1

Svaberg
Svaberg

Reputation: 1681

In recent versions of VS Code there is an API for populating the outline view without needing to depend on third party extensions (except the one you need to write yourself).

This works by registering a DocumentSymbolProvider.

export function activate(context: vscode.ExtensionContext) {
    context.subscriptions.push(
        vscode.languages.registerDocumentSymbolProvider(
            {scheme: "file", language: "swmf-config"}, 
            new SwmfConfigDocumentSymbolProvider())
    );
}

This permits a flat structure or a tree structure in the outline view (the two cannot be mixed).

class SwmfConfigDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
    public provideDocumentSymbols(
        document: vscode.TextDocument,
        token: vscode.CancellationToken): Promise<vscode.DocumentSymbol[]> {
        return new Promise((resolve, reject) => {
            let symbols: vscode.DocumentSymbol[] = [];
            for (var i = 0; i < document.lineCount; i++) {
                var line = document.lineAt(i);
                if (line.text.startsWith("#")) {
                    let symbol = new vscode.DocumentSymbol(
                        line.text, 'Component',
                        vscode.SymbolKind.Function,
                        line.range, line.range)
                    symbols.push(symbol)
                }
            }
            resolve(symbols);
        });
    }
}

For a small, fully working example giving a tree structure in the outline view, see https://github.com/svaberg/SWMF-grammar

Upvotes: 21

Yuli Hua
Yuli Hua

Reputation: 721

Okay, my request is now solved.

The CodeMap extension, is basically the extension I'm looking for.

I followed their guide on https://github.com/oleg-shilo/codemap.vscode/wiki/Adding-custom-mappers

I created a custom dedicated mapper "mapper_X.js" saved it to an arbitrary location, and in my vscode user-settings I pasted "codemap.X": "mylocation\\mapper_X.js", (as described in the github guide). I then opened up a new file, saved it as untitled.X and typed some syntax (the example code in my question), now I could see my custom outline.

As could be seen in the result-link below I have (deliberately) not defined my mapper to consider any other cases yet. My mapper is still in its infancy state. Just thought I'd share my findings before I forget I posted this question...

result

Upvotes: 25

Related Questions