Skip to content

Commit

Permalink
Merge pull request #9 from wilsonneto-dev/wn/add-markdown-support
Browse files Browse the repository at this point in the history
feat: Add markdown support
  • Loading branch information
wilsonneto-dev authored Oct 19, 2024
2 parents bcef986 + 21c715d commit 1c8bcc0
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 6 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**Navigation Power-ups** is a Visual Studio Code extension that enhances your file navigation experience by adding a left bar navigation tree to files that haven't it natively. The extension is designed to support a variety of file types, offering an easy-to-use interface for jumping between sections, headers, or requests within supported files.

Currently, the extension supports `.http` files, with plans to extend compatibility to Markdown files (`.md`) and others in the future.
Currently, the extension supports `.http` and `.md` files, feel free to add any other file types you miss.

## Features

Expand All @@ -16,7 +16,7 @@ Currently, the extension supports `.http` files, with plans to extend compatibil
| File Type | Description | Navigation Rules |
|-----------|--------------------------------------------------|----------------------------------------------------------------------------------|
| `.http` | HTTP request files | Navigation based on `###` for requests. Headers with more than `###` are sections.|
| `.md` | Markdown files *(Upcoming)* | Navigation based on `#`, `##`, `###`, etc. headers. |
| `.md` | Markdown files | Navigation based on headers (`#`, `##`, `###`, etc). |
| More | Additional file types *(Planned for future)* | TBD |

## Setup
Expand All @@ -29,14 +29,21 @@ Currently, the extension supports `.http` files, with plans to extend compatibil

4. **Real-time Updates**: As you make changes to the file (such as adding new sections, editing headers, or removing requests), the tree will update in real time to reflect these changes.

### Markdown Files

To be able to see the navigation left menu in markdown files, you just need to open the file and click in the plugin icon in the left bar.

![Markdown file support](image.png)


### HTTP Files

In `.http` files, the extension uses the number of `#` characters to differentiate between sections and requests:

- **Sections**: Any headers with more than three `#` characters (e.g., `#### Section`) are treated as higher-level sections.
- **Requests**: Headers with exactly three `#` characters (e.g., `### request`) are treated as individual HTTP requests.

### Example of `.http` file structure:
#### Example of `.http` file structure:

![Http file example](./images/readme-httpfile-eg.png)

Expand Down
Binary file added images/readme-markdown-eg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "navigation-powerups",
"displayName": "Navigation Power-ups",
"description": "Not a big extension, but hey, it keeps you from getting lost in the maze of .http requests. Navigate smarter, not harder :)",
"version": "1.0.1",
"description": "Navigate smarter, not harder :)",
"version": "1.1.0",
"repository": "https://github.com/wilsonneto-dev/navigation-powerups",
"publisher": "wilsonneto-dev",
"icon": "images/icon.png",
Expand Down
4 changes: 3 additions & 1 deletion src/parsers/FileToNavigationParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import INavigationParserStrategy from "../model/INavigationParserStrategy";
import HttpFileToNavParser from "./httpfiles/HttpFileToNavParser";
import { IFile } from "../model/IFile";
import NavigationNode from "../model/NavigationNode";
import MarkdownFileToNavParser from "./markdown/MarkdownFileToNavParser";

const navigationParsers : { [extension: string]: INavigationParserStrategy } = {
'http': new HttpFileToNavParser()
'http': new HttpFileToNavParser(),
'md': new MarkdownFileToNavParser()
};

/**
Expand Down
47 changes: 47 additions & 0 deletions src/parsers/markdown/MarkdownFileToNavParser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import NavigationNode from "../../model/NavigationNode";
import MarkdownFileToNavParser from "./MarkdownFileToNavParser";

const exampleMarkdown = `
# Title 1
## Subtitle 1.1
## Subtitle 1.2
# Title 2
## Subtitle 2.1
### Subsection 2.1.1
## Subtitle 2.2
# Title 3
## Subtitle 3.1
`;

describe('MarkdownFileToNavParser', () => {
let parsedRoot: NavigationNode;

beforeAll(() => {
const markdownParser = new MarkdownFileToNavParser();
parsedRoot = markdownParser.parse(exampleMarkdown);
console.log(parsedRoot);
});

test('should parse the top-level titles properly', () => {
expect(parsedRoot.getChildren().length).toBe(3);
expect(parsedRoot.getChildren()[0].getName()).toBe('Title 1');
expect(parsedRoot.getChildren()[1].getName()).toBe('Title 2');
expect(parsedRoot.getChildren()[2].getName()).toBe('Title 3');
});

test('should parse the subtitles properly', () => {
const title1 = parsedRoot.getChildren()[0];
expect(title1.getChildren().length).toBe(2);
expect(title1.getChildren()[0].getName()).toBe('Subtitle 1.1');
expect(title1.getChildren()[1].getName()).toBe('Subtitle 1.2');

const title2 = parsedRoot.getChildren()[1];
expect(title2.getChildren().length).toBe(2);
expect(title2.getChildren()[0].getName()).toBe('Subtitle 2.1');
expect(title2.getChildren()[0].getChildren().length).toBe(1); // Subsection under Subtitle 2.1
expect(title2.getChildren()[0].getChildren()[0].getName()).toBe('Subsection 2.1.1');
expect(title2.getChildren()[1].getName()).toBe('Subtitle 2.2');
});
});
42 changes: 42 additions & 0 deletions src/parsers/markdown/MarkdownFileToNavParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import INavigationParserStrategy from "../../model/INavigationParserStrategy";
import NavigationNode from "../../model/NavigationNode";

class MarkdownFileToNavParser implements INavigationParserStrategy {
public parse(text: string): NavigationNode {
const root = new NavigationNode("Markdown Document");
const parents: NavigationNode[] = [];
parents.push(root);

const lines = text.split("\n");
lines.forEach((line, index) => {
const lineNumber = index;

if (this.isSectionHeader(line)) {
const header = new NavigationNode(this.getName(line), this.getLevel(line), lineNumber);
while (header.getLevel() <= parents.at(-1)!.getLevel()) {
parents.pop();
}

parents.at(-1)!.addChild(header);
parents.push(header);
}
});

return root;
}

private isSectionHeader(line: string): boolean {
return line.trim().startsWith("#") && line.trim().split(" ").length > 1;
}

private getName(line: string): string {
return line.trim().split(" ").slice(1).join(" ").trim();
}

private getLevel(line: string): number {
const numberOfSharps = line.trim().split(" ")[0].split('#').length - 1;
return numberOfSharps; // Levels are 0-based for easier hierarchy
}
}

export default MarkdownFileToNavParser;

0 comments on commit 1c8bcc0

Please sign in to comment.