├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── README.md ├── images ├── icon.png └── screenshot.jpg ├── license.txt ├── package.json ├── src └── extension.ts ├── test ├── extension.test.ts └── index.ts ├── tsconfig.json ├── tsd.json ├── typings ├── lodash │ └── lodash.d.ts ├── node.d.ts ├── tsd.d.ts └── vscode-typings.d.ts └── vsc-extension-quickstart.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | *.vsix -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outDir": "out/src", 14 | "preLaunchTask": "npm" 15 | }, 16 | { 17 | "name": "Launch Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], 22 | "stopOnEntry": false, 23 | "sourceMaps": true, 24 | "outDir": "out/test", 25 | "preLaunchTask": "npm" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version 10 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "0.1.0", 12 | 13 | // we want to run npm 14 | "command": "npm", 15 | 16 | // the command is a shell script 17 | "isShellCommand": true, 18 | 19 | // show the output window only if unrecognized errors occur. 20 | "showOutput": "silent", 21 | 22 | // we run the custom script "compile" as defined in package.json 23 | "args": ["run", "compile", "--loglevel", "silent"], 24 | 25 | // The tsc compiler is started in watching mode 26 | "isWatching": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | images/** 11 | *.vsix -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Indent Guides for Visual Studio Code 2 | This extension adds indent guides to the editor. 3 | 4 | ![Screenshot](/images/screenshot.jpg?raw=true "Screenshot") 5 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoeRobich/vscode-indent-guides/71e852e1291f7ad4f9c303268c7a5986bac7c077/images/icon.png -------------------------------------------------------------------------------- /images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoeRobich/vscode-indent-guides/71e852e1291f7ad4f9c303268c7a5986bac7c077/images/screenshot.jpg -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Joey Robichaud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "indent-guides", 3 | "displayName": "Indent Guides", 4 | "description": "This extensions adds indent guides to the editor.", 5 | "version": "0.0.5", 6 | "publisher": "JoeyRobichaud", 7 | "icon": "/images/icon.png", 8 | "galleryBanner": { 9 | "color": "#1E1E1E", 10 | "theme": "dark" 11 | }, 12 | "license": "SEE LICENSE IN LICENSE.TXT", 13 | "bugs": { 14 | "url": "https://github.com/JoeRobich/vscode-indent-guides/issues" 15 | }, 16 | "homepage": "https://github.com/JoeRobich/vscode-indent-guides/blob/master/README.md", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/JoeRobich/vscode-indent-guides.git" 20 | }, 21 | "engines": { 22 | "vscode": "^0.10.1" 23 | }, 24 | "categories": [ 25 | "Other" 26 | ], 27 | "activationEvents": [ 28 | "*" 29 | ], 30 | "main": "./out/src/extension", 31 | "contributes": { 32 | "configuration": { 33 | "type": "object", 34 | "title": "Indent Guide configuration", 35 | "properties": { 36 | "indent-guide.style": { 37 | "type": "string", 38 | "default": "solid", 39 | "description": "Determines how to render the indent guides. Valid values are \"solid\", \"dotted\", and \"dashed\"." 40 | }, 41 | "indent-guide.color": { 42 | "type": "string", 43 | "default": "currentColor", 44 | "description": "Specifies the color of the indent guides." 45 | } 46 | } 47 | } 48 | }, 49 | "scripts": { 50 | "vscode:prepublish": "node ./node_modules/vscode/bin/compile", 51 | "compile": "node ./node_modules/vscode/bin/compile -watch -p ./" 52 | }, 53 | "dependencies": { 54 | "lodash": "^3.10.1" 55 | }, 56 | "devDependencies": { 57 | "typescript": "^1.6.2", 58 | "vscode": "0.10.x" 59 | } 60 | } -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import {workspace, window, commands, Position, Range, TextLine, TextDocument, TextEditor, TextEditorDecorationType, ExtensionContext, Disposable} from 'vscode'; 2 | import {range, debounce} from 'lodash'; 3 | 4 | export function activate(context:ExtensionContext) { 5 | let guideDecorator = new GuideDecorator(); 6 | 7 | // Hook Events 8 | let subscriptions:Disposable[] = []; 9 | workspace.onDidChangeTextDocument(debounce(onEditorChange.bind(null, guideDecorator), 50), this, subscriptions); 10 | window.onDidChangeActiveTextEditor(guideDecorator.updateActiveEditor, guideDecorator, subscriptions); 11 | workspace.onDidChangeConfiguration(guideDecorator.reset, guideDecorator, subscriptions); 12 | let eventDisposable = Disposable.from(...subscriptions); 13 | 14 | // Register Disposables 15 | context.subscriptions.push(guideDecorator); 16 | context.subscriptions.push(eventDisposable); 17 | 18 | guideDecorator.reset(); 19 | } 20 | 21 | function onEditorChange(guideDecorator:GuideDecorator) { 22 | let activeEditor = window.activeTextEditor; 23 | let cursorPosition = activeEditor.selection.active; 24 | let cursorLine = activeEditor.document.lineAt(cursorPosition.line); 25 | let indentation = cursorLine.isEmptyOrWhitespace ? cursorLine.text.length : cursorLine.firstNonWhitespaceCharacterIndex; 26 | 27 | if (!activeEditor.selection.isEmpty || // Change to large area, possibly a snippet. 28 | cursorPosition.character <= indentation) // Change within the indentation area. 29 | guideDecorator.updateActiveEditor(); 30 | } 31 | 32 | class GuideDecorator { 33 | private _indentGuide:TextEditorDecorationType = null; 34 | 35 | public updateActiveEditor():void { 36 | this.updateIndentGuides(window.activeTextEditor); 37 | } 38 | 39 | updateVisibleEditors():void { 40 | for (let editor of window.visibleTextEditors) 41 | this.updateIndentGuides(editor); 42 | } 43 | 44 | updateIndentGuides(editor:TextEditor):void { 45 | if (editor === null) 46 | return; 47 | 48 | let guideStops:Range[] = this.getIndentedLines(editor.document) 49 | .map(line => this.getGuideStops(line, editor.options.tabSize)) 50 | .reduce((all, ranges) => all.concat(ranges), []); 51 | 52 | editor.setDecorations(this._indentGuide, guideStops); 53 | } 54 | 55 | getIndentedLines(document:TextDocument):TextLine[] { 56 | return range(0, document.lineCount) 57 | .map(lineNumber => document.lineAt(lineNumber)) 58 | .filter(line => line.firstNonWhitespaceCharacterIndex != 0); 59 | } 60 | 61 | getGuideStops(line:TextLine, tabSize:number):Range[] { 62 | let stopSize = line.text[0] === '\t' ? 1 : tabSize; // Currently expects the indentation to be either tabs or spaces. Produces strange output when both are used on the same line. 63 | let indentation = line.isEmptyOrWhitespace ? line.text.length : line.firstNonWhitespaceCharacterIndex; 64 | let depth = indentation / stopSize; 65 | 66 | return range(1, depth) 67 | .map(stop => new Position(line.lineNumber, stop * stopSize)) 68 | .map(position => new Range(position, position)); 69 | } 70 | 71 | public reset() { 72 | this.dispose(); 73 | this._indentGuide = this.createIndentGuideDecoration(); 74 | this.updateVisibleEditors(); 75 | } 76 | 77 | createIndentGuideDecoration():TextEditorDecorationType { 78 | var configuration:any = workspace.getConfiguration("indent-guide"); 79 | return window.createTextEditorDecorationType({ 80 | borderColor: configuration.color, 81 | borderWidth: "0 0 0 1px", 82 | borderStyle: configuration.style 83 | }); 84 | } 85 | 86 | dispose() { 87 | if (this._indentGuide !== null) 88 | this._indentGuide.dispose(); 89 | } 90 | } -------------------------------------------------------------------------------- /test/extension.test.ts: -------------------------------------------------------------------------------- 1 | // 2 | // Note: This example test is leveraging the Mocha test framework. 3 | // Please refer to their documentation on https://mochajs.org/ for help. 4 | // 5 | 6 | // The module 'assert' provides assertion methods from node 7 | import * as assert from 'assert'; 8 | 9 | // You can import and use all API from the 'vscode' module 10 | // as well as import your extension to test it 11 | import * as vscode from 'vscode'; 12 | import * as myExtension from '../src/extension'; 13 | 14 | // Defines a Mocha test suite to group tests of similar kind together 15 | suite("Extension Tests", () => { 16 | 17 | // Defines a Mocha unit test 18 | test("Something 1", () => { 19 | assert.equal(-1, [1, 2, 3].indexOf(5)); 20 | assert.equal(-1, [1, 2, 3].indexOf(0)); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES5", 5 | "outDir": "out", 6 | "noLib": true, 7 | "sourceMap": true 8 | }, 9 | "exclude": [ 10 | "node_modules" 11 | ] 12 | } -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "lodash/lodash.d.ts": { 9 | "commit": "8ea42cd8bb11863ed6f242d67c502288ebc45a7b" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /typings/node.d.ts: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /typings/tsd.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /typings/vscode-typings.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your first VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * press `F5` to open a new window with your extension loaded 16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` 17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension 18 | * find output from your extension in the debug console 19 | 20 | ## Make changes 21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` 22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes 23 | 24 | ## Explore the API 25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` 26 | 27 | ## Run tests 28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` 29 | * press `F5` to run the tests in a new window with your extension loaded 30 | * see the output of the test result in the debug console 31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder 32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts` 33 | * you can create folders inside the `test` folder to structure your tests any way you want --------------------------------------------------------------------------------