Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add everything for the initial release #1

Merged
merged 1 commit into from
Dec 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
os:
- osx
- linux

language: node_js

node_js:
- "6.3"
- node

before_install:
- if [ $TRAVIS_OS_NAME == "linux" ]; then
export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0;
sh -e /etc/init.d/xvfb start;
sleep 3;
fi

after_success: npm run coverage:report

sudo: false
22 changes: 22 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// A launch configuration that launches the extension inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/test" ],
"stopOnEntry": false
}
]
}
8 changes: 8 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.vscode/**
.vscode-test/**
coverage/**
instrumented/**
static/**
test/**
.gitignore
.travis.yml
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,30 @@
# vscode-lowercase
Convert selection to lowercase in [Visual Studio Code](https://github.com/Microsoft/vscode).

[![Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](http://standardjs.com/)
[![Travis](https://img.shields.io/travis/ruiquelhas/vscode-lowercase.svg?style=flat-square)](https://travis-ci.org/ruiquelhas/vscode-lowercase)
[![Codecov](https://img.shields.io/codecov/c/github/ruiquelhas/vscode-lowercase.svg?style=flat-square)](https://codecov.io/gh/ruiquelhas/vscode-lowercase)

## How it works

### Using the command palette
![Command palette](static/palette.gif)

### Using a keyboard shortcut
![Keyboard shortcut](static/shortcut.gif)

The default keboard shortcut is set to `alt+shift+l`, but you can change it to something else by overriding the `key` value of the `lowercase.tolowercase` command.

## Contributing
Contributions are welcome, either via [issues](https://github.com/ruiquelhas/vscode-lowercase/issues/new) or [pull requests](https://github.com/ruiquelhas/vscode-lowercase/compare).

For any code addition or change, follow the [style guide](http://standardjs.com/rules.html), add the respective tests and make sure the existing ones still pass.

### Running the tests

```sh
$ npm t
```

## License
MIT
29 changes: 29 additions & 0 deletions lib/extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { Range, commands } = require('vscode')

function activate (context) {
const disposable = commands.registerTextEditorCommand('lowercase.toLowerCase', toLowerCase)

context.subscriptions.push(disposable)
}

function toLowerCase (editor) {
return editor.edit(builder => {
editor.selections.forEach(selection => {
const range = new Range(selection.start, selection.end)
const text = editor.document.getText(range) || ''
const matches = text.match(/(?=.*[a-zA-Z]).+/g)

if (!matches || !matches.length) {
return
}

builder.replace(selection, text.toLowerCase())
})
})
}

function deactivate () {
return
}

module.exports = { activate, toLowerCase, deactivate }
72 changes: 72 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"name": "vscode-lowercase",
"displayName": "lowercase",
"description": "Convert selection to lowercase",
"version": "0.0.1",
"author": "Rui Quelhas <[email protected]> (https://www.ruiquelhas.xyz)",
"publisher": "ruiquelhas",
"engines": {
"node": "^6.3.0",
"vscode": "^1.7.1"
},
"categories": [
"Formatters",
"Keymaps",
"Other"
],
"activationEvents": [
"onCommand:lowercase.toLowerCase"
],
"main": "./lib/extension",
"contributes": {
"commands": [
{
"command": "lowercase.toLowerCase",
"title": "Convert selection to lowercase"
}
],
"keybindings": [
{
"command": "lowercase.toLowerCase",
"key": "alt+shift+l",
"mac": "alt+shift+l"
}
]
},
"scripts": {
"coverage:check": "npm run coverage:instrument && ISTANBUL_REPORT_DIR=coverage ISTANBUL_REPORTERS='text-summary,html,lcov' npm run coverage:run",
"coverage:instrument": "istanbul instrument lib/ -o instrumented/",
"coverage:report": "npm run coverage:check && cat coverage/lcov.info | codecov",
"coverage:run": "NODE_PATH=instrumented NODE_ENV=coverage node ./node_modules/vscode/bin/test && rimraf instrumented",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "NODE_PATH=lib node ./node_modules/vscode/bin/test"
},
"devDependencies": {
"codecov.io": "^0.1.6",
"fs-promise": "^1.0.0",
"istanbul": "^0.4.5",
"lodash.isequal": "^4.4.0",
"mocha": "^3.1.2",
"mocha-istanbul": "^0.3.0",
"proxyquire": "^1.7.10",
"rimraf": "^2.5.4",
"standard": "^8.5.0",
"testdouble": "^1.9.0",
"vscode": "^1.0.3"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/ruiquelhas/vscode-lowercase/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ruiquelhas/vscode-lowercase.git"
},
"homepage": "https://github.com/ruiquelhas/vscode-lowercase#readme",
"keywords": [
"case",
"extension",
"lowercase",
"vscode"
]
}
Binary file added static/palette.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/shortcut.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const testRunner = require('vscode/lib/testrunner')

const options = {
ui: 'tdd',
useColors: true
}

if (process.env.NODE_ENV === 'coverage') {
options.reporter = 'mocha-istanbul'
}

testRunner.configure(options)

module.exports = testRunner
64 changes: 64 additions & 0 deletions test/integration/extension.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* global setup, suite, teardown, test */

const { Position, Range, Selection, window, workspace } = require('vscode')
const assert = require('assert')
const fs = require('fs-promise')
const lowercase = require('extension')
const os = require('os')
const path = require('path')

suite('integration tests', () => {
let editor

setup('create canvas', () => {
if (window.activeTextEditor) {
editor = window.activeTextEditor
return
}

const newFile = path.join(os.tmpdir(), 'foobar.txt')

return fs.createFile(newFile)
.then(() => {
return workspace.openTextDocument(newFile)
})
.then(doc => {
return window.showTextDocument(doc)
})
.then(activeTextEditor => {
editor = activeTextEditor
})
})

teardown('reset canvas', () => {
return editor.edit(builder => {
builder.insert(new Position(0, 0), '')
})
})

test('converts selections to lowercase', () => {
const content = 'FOO\nBAR'
const fooStart = new Position(0, 0)
const fooEnd = new Position(0, 3)
const barStart = new Position(1, 0)
const barEnd = new Position(1, 3)

return editor
.edit(builder => {
builder.insert(new Position(0, 0), content)
})
.then(() => {
editor.selections = [
new Selection(fooStart, fooEnd),
new Selection(barStart, barEnd)
]

return lowercase.toLowerCase(editor)
})
.then(result => {
assert.ok(result)
assert.equal(editor.document.getText(new Range(fooStart, fooEnd)), 'foo')
assert.equal(editor.document.getText(new Range(barStart, barEnd)), 'bar')
})
})
})
124 changes: 124 additions & 0 deletions test/unit/extension.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* global setup, suite, teardown, test */

const { Position, Range, Selection } = require('vscode')
const assert = require('assert')
const isEqual = require('lodash.isequal')
const lowercase = require('extension')
const proxyquire = require('proxyquire')
const td = require('testdouble')

suite('unit tests', () => {
teardown('reset fakes', () => {
td.reset()
})

suite('activate', () => {
test('calls the `toLowerCase` command', () => {
const context = { subscriptions: [] }
const disposable = { foo: 'bar' }
const registerTextEditorCommand = td.function()
const lowercase = proxyquire('extension', {
vscode: {
commands: { registerTextEditorCommand }
}
})

td.when(registerTextEditorCommand('lowercase.toLowerCase', lowercase.toLowerCase))
.thenReturn(disposable)

lowercase.activate(context)

assert.deepEqual(context, { subscriptions: [disposable] }, 'adds extension to the subscriptions')
})
})

suite('toLowerCase', () => {
let edit, getText, replace

setup('create fakes', () => {
edit = td.function()
getText = td.function()
replace = td.function()
})

test('does nothing if there are no selections', () => {
td.when(edit(td.callback({ replace }))).thenResolve(true)

return lowercase.toLowerCase({ edit, selections: [] }).then(() => {
td.verify(getText(), { ignoreExtraArgs: true, times: 0 })
td.verify(replace(), { ignoreExtraArgs: true, times: 0 })
})
})

test('does nothing for empty selections', () => {
const selection = new Selection(new Position(0, 0), new Position(0, 0))
const range = new Range(selection.start, selection.end)
const editor = { document: { getText }, edit, selections: [selection] }

td.when(edit(td.callback({ replace }))).thenResolve(true)
td.when(getText(td.matchers.argThat(r => isEqual(r, range)))).thenReturn('')

return lowercase.toLowerCase(editor).then(() => {
td.verify(replace(), { ignoreExtraArgs: true, times: 0 })
})
})

test('does nothing for selections not containing alphabetic characters', () => {
const selection = new Selection(new Position(0, 0), new Position(0, 2))
const range = new Range(selection.start, selection.end)
const editor = { document: { getText }, edit, selections: [selection] }

td.when(edit(td.callback({ replace }))).thenResolve(true)
td.when(getText(td.matchers.argThat(r => isEqual(r, range)))).thenReturn(' 1*')

return lowercase.toLowerCase(editor).then(() => {
td.verify(replace(), { ignoreExtraArgs: true, times: 0 })
})
})

test('converts multiple non-empty selections to lowercase', () => {
// Editor contents:
// FOO BAR
const start1 = new Position(0, 0)
const start2 = new Position(0, 4)
const end1 = new Position(0, 2)
const end2 = new Position(0, 6)
const selection1 = new Selection(start1, end1)
const selection2 = new Selection(start2, end2)
const editor = { document: { getText }, edit, selections: [selection1, selection2] }

td.when(edit(td.callback({ replace }))).thenResolve(true)
td.when(getText(new Range(start1, end1))).thenReturn('FOO')
td.when(getText(new Range(start2, end2))).thenReturn('BAR')

return lowercase.toLowerCase(editor).then(() => {
td.verify(replace(selection1, 'foo'), { times: 1 })
td.verify(replace(selection2, 'bar'), { times: 1 })
})
})

test('converts single multi-line selections to lowercase', () => {
// Editor contents:
// FOO
//
// BAR
const start = new Position(0, 0)
const end = new Position(2, 4)
const selection = new Selection(start, end)
const editor = { document: { getText }, edit, selections: [selection] }

td.when(edit(td.callback({ replace }))).thenResolve(true)
td.when(getText(new Range(start, end))).thenReturn('FOO\nBAR')

return lowercase.toLowerCase(editor).then(() => {
td.verify(replace(selection, 'foo\nbar'), { times: 1 })
})
})
})

suite('deactivate', () => {
test('returns `undefined`', () => {
assert.equal(lowercase.deactivate(), undefined)
})
})
})