Skip to content

Commit

Permalink
feat: "Render Conditionally" command for JSX (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
ob1 authored and borislit committed Aug 5, 2018
1 parent 064d740 commit ab8a7cf
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
23 changes: 21 additions & 2 deletions src/code-actions.ts
@@ -1,12 +1,13 @@
import { SnippetString } from 'vscode';
import { showDirectoryPicker } from "./directories-picker";
import { showFilePicker } from "./file-picker";
import { activeEditor, selectedText, activeFileName, openFile, selectedTextStart, selectedTextEnd, showErrorMessage, showInformationMessage } from "./editor";
import { activeEditor, selectedText, activeFileName, openFile, selectedTextStart, selectedTextEnd, showErrorMessage, showInformationMessage, allText } from "./editor";
import { statelessToStateful } from "./modules/statless-to-stateful";
import { statefulToStateless } from './modules/stateful-to-stateless'
import { shouldSwitchToTarget, shouldBeConsideredJsFiles } from "./settings";
import { replaceTextInFile, appendTextToFile, prependTextToFile, removeContentFromFileAtLineAndColumn } from "./file-system";
import { getIdentifier, generateImportStatementFromFile, transformJSIntoExportExpressions } from "./parsing";
import { createComponentInstance, wrapWithComponent } from "./modules/jsx";
import { createComponentInstance, wrapWithComponent, isRangeContainedInJSXExpression, isJSXExpression } from "./modules/jsx";
import * as relative from 'relative';
import * as path from 'path';

Expand All @@ -31,6 +32,24 @@ export async function extractJSXToComponent() {
}
}

export async function wrapJSXWithCondition() {
var editor = activeEditor();
if (!editor) {
return; // No open text editor
}

try {
const selText = selectedText();
const isParentJSXExpression = isRangeContainedInJSXExpression(allText(), selectedTextStart(), selectedTextEnd());
const conditionalJSX = isJSXExpression(selText) ? selText : `<>${selText}</>`;
const snippetInnerText = `\n$\{1:true\}\n? ${conditionalJSX}\n: $\{2:null\}\n`;
const snippetText = isParentJSXExpression ? `{${snippetInnerText}}` : `(${snippetInnerText})`;
await editor.insertSnippet(new SnippetString(snippetText));
} catch (e) {
handleError(e);
}
}

export async function extractToFile() {
var editor = activeEditor();
if (!editor) {
Expand Down
4 changes: 4 additions & 0 deletions src/editor.ts
Expand Up @@ -44,6 +44,10 @@ export function selectedText() {
return editor.document.getText(selection);
}

export function allText() {
const editor = vscode.window.activeTextEditor;
return editor.document.getText();
}

export function showInputBox(defaultValue, placeHolder) {
return vscode.window.showInputBox({
Expand Down
7 changes: 6 additions & 1 deletion src/extension.ts
@@ -1,4 +1,4 @@
import { extractToFile, statelessToStatefulComponent, statefulToStatelessComponent, extractJSXToComponent } from './code-actions';
import { extractToFile, statelessToStatefulComponent, statefulToStatelessComponent, extractJSXToComponent, wrapJSXWithCondition } from './code-actions';
'use strict';

import * as vscode from 'vscode';
Expand All @@ -19,6 +19,9 @@ export class CompleteActionProvider implements vscode.CodeActionProvider {
return [{
command: 'extension.glean.react.extract-component',
title: 'Extract Component'
}, {
command: 'extension.glean.react.render-conditionally',
title: 'Render Conditionally'
}];
}

Expand Down Expand Up @@ -51,6 +54,8 @@ export function activate(context: vscode.ExtensionContext) {

vscode.commands.registerCommand('extension.glean.react.extract-component', extractJSXToComponent);

vscode.commands.registerCommand('extension.glean.react.render-conditionally', wrapJSXWithCondition);

vscode.commands.registerCommand('extension.glean.react.stateless-to-stateful', statelessToStatefulComponent);

vscode.commands.registerCommand('extension.glean.react.stateful-to-stateless', statefulToStatelessComponent);
Expand Down
40 changes: 40 additions & 0 deletions src/modules/jsx.ts
Expand Up @@ -31,6 +31,46 @@ export function isJSX(code) {
return ast && ast.expression && t.isJSX(ast.expression);
}

export function isJSXExpression(code) {
try {
const ast = template.ast(code, defaultTemplateOptions);
return ast && ast.expression && t.isJSX(ast.expression);
} catch (e) {
return false;
}
}

export function isRangeContainedInJSXExpression(code, start, end) {
try {
const ast = codeToAst(code)
const path = findContainerPath(ast, start, end)
return path && t.isJSX(path.node) && t.isExpression(path.node);
} catch (e) {
return false
}
}

function findContainerPath(ast, start, end) {
let foundPath = null;
const visitor = {
exit(path) {
if (!foundPath && pathContains(path, start, end)) {
foundPath = path
}
}
}

traverse(ast, visitor);
return foundPath;
}

function pathContains(path, start, end) {
const pathStart = path.node.loc.start
const pathEnd = path.node.loc.end
return ((pathStart.line < start.line) || (pathStart.line === start.line && pathStart.column < start.character))
&& ((pathEnd.line > end.line) || (pathEnd.line === end.line && pathEnd.column > end.character))
}

function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
Expand Down

0 comments on commit ab8a7cf

Please sign in to comment.