GeezerVison is showing github.com — changes to #325 · vdt/pythonVSCode@5112249
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: pythonFiles/refactor.py +21Lines changed: 21 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -166,6 +166,23 @@ def __init__(self): | 166 | 166 | self.default_sys_path = sys.path | 167 | 167 | self._input = io.open(sys.stdin.fileno(), encoding='utf-8') | 168 | 168 |
| | 169 | + def _rename(self, filePath, start, newName): | | 170 | + """ | | 171 | + Extracts a variale | | 172 | + """ | | 173 | + project = rope.base.project.Project( | | 174 | + WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False) | | 175 | + resourceToRefactor = libutils.path_to_resource(project, filePath) | | 176 | + refactor = RenameRefactor( | | 177 | + project, resourceToRefactor, startOffset=start, newName=newName) | | 178 | + refactor.refactor() | | 179 | + changes = refactor.changes | | 180 | + project.close() | | 181 | + valueToReturn = [] | | 182 | + for change in changes: | | 183 | + valueToReturn.append({'diff': change.diff}) | | 184 | + return valueToReturn | | 185 | + | 169 | 186 | def _extractVariable(self, filePath, start, end, newName): | 170 | 187 | """ | 171 | 188 | Extracts a variale | @@ -225,6 +242,10 @@ def _process_request(self, request): | 225 | 242 |
| 226 | 243 | if lookup == '': | 227 | 244 | pass | | 245 | + elif lookup == 'rename': | | 246 | + changes = self._rename(request['file'], int( | | 247 | + request['start']), request['name']) | | 248 | + return self._write_response(self._serialize(request['id'], changes)) | 228 | 249 | elif lookup == 'extract_variable': | 229 | 250 | changes = self._extractVariable(request['file'], int( | 230 | 251 | request['start']), int(request['end']), request['name']) | |
Collapse file Copy file name to clipboardExpand all lines: src/client/common/editor.ts +80-8Lines changed: 80 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | | -import {TextEdit, Position, Range, TextDocument} from 'vscode'; | | 1 | +import {TextEdit, Position, Range, TextDocument, WorkspaceEdit} from 'vscode'; | | 2 | +import * as vscode from 'vscode'; | 2 | 3 | import * as dmp from 'diff-match-patch'; | 3 | 4 | import {EOL} from 'os'; | 4 | 5 | import * as fs from 'fs'; | @@ -60,7 +61,7 @@ export function getTextEditsFromPatch(before: string, patch: string): TextEdit[] | 60 | 61 | if (!Array.isArray(patches) || patches.length === 0) { | 61 | 62 | throw new Error('Unable to parse Patch string'); | 62 | 63 | } | 63 | | - let textEdits = []; | | 64 | + let textEdits: TextEdit[] = []; | 64 | 65 |
| 65 | 66 | // Add line feeds | 66 | 67 | // & build the text edits | @@ -69,24 +70,95 @@ export function getTextEditsFromPatch(before: string, patch: string): TextEdit[] | 69 | 70 | diff[1] += EOL; | 70 | 71 | }); | 71 | 72 |
| 72 | | - textEdits = textEdits.concat(getTextEditsInternal(before, patch.diffs, patch.start1)); | | 73 | + getTextEditsInternal(before, patch.diffs, patch.start1).forEach(edit => textEdits.push(edit.apply())); | 73 | 74 | }); | 74 | 75 |
| 75 | 76 | return textEdits; | 76 | 77 | } | | 78 | +export function getWorkspaceEditsFromPatch(filePatches: string[]): WorkspaceEdit { | | 79 | + const workspaceEdit = new WorkspaceEdit(); | | 80 | + filePatches.forEach(patch => { | | 81 | + const indexOfAtAt = patch.indexOf('@@'); | | 82 | + if (indexOfAtAt === -1) { | | 83 | + return; | | 84 | + } | | 85 | + const fileNameLines = patch.substring(0, indexOfAtAt).split(/\r?\n/g) | | 86 | + .map(line => line.trim()) | | 87 | + .filter(line => line.length > 0 && | | 88 | + line.toLowerCase().endsWith('.py') && | | 89 | + line.indexOf(' a') > 0); | | 90 | + | | 91 | + if (patch.startsWith('---')) { | | 92 | + // Strip the first two lines | | 93 | + patch = patch.substring(indexOfAtAt); | | 94 | + } | | 95 | + if (patch.length === 0) { | | 96 | + return; | | 97 | + } | | 98 | + // We can't find the find name | | 99 | + if (fileNameLines.length === 0) { | | 100 | + return; | | 101 | + } | | 102 | + | | 103 | + let fileName = fileNameLines[0].substring(fileNameLines[0].indexOf(' a') + 3).trim(); | | 104 | + fileName = path.isAbsolute(fileName) ? fileName : path.resolve(vscode.workspace.rootPath, fileName); | | 105 | + if (!fs.existsSync(fileName)) { | | 106 | + return; | | 107 | + } | | 108 | + | | 109 | + // Remove the text added by unified_diff | | 110 | + // # Work around missing newline (http://bugs.python.org/issue2142). | | 111 | + patch = patch.replace(/\\ No newline at end of file[\r\n]/, ''); | | 112 | + | | 113 | + let d = new dmp.diff_match_patch(); | | 114 | + let patches: any[] = patch_fromText.call(d, patch); | | 115 | + if (!Array.isArray(patches) || patches.length === 0) { | | 116 | + throw new Error('Unable to parse Patch string'); | | 117 | + } | | 118 | + | | 119 | + const fileSource = fs.readFileSync(fileName).toString('utf8'); | | 120 | + const fileUri = vscode.Uri.file(fileName); | | 121 | + | | 122 | + // Add line feeds | | 123 | + // & build the text edits | | 124 | + patches.forEach(patch => { | | 125 | + patch.diffs.forEach(diff => { | | 126 | + diff[1] += EOL; | | 127 | + }); | | 128 | + | | 129 | + getTextEditsInternal(fileSource, patch.diffs, patch.start1).forEach(edit => { | | 130 | + switch (edit.action) { | | 131 | + case EDIT_DELETE: { | | 132 | + workspaceEdit.delete(fileUri, new Range(edit.start, edit.end)); | | 133 | + } | | 134 | + case EDIT_INSERT: { | | 135 | + workspaceEdit.insert(fileUri, edit.start, edit.text); | | 136 | + } | | 137 | + case EDIT_REPLACE: { | | 138 | + workspaceEdit.replace(fileUri, new Range(edit.start, edit.end), edit.text); | | 139 | + } | | 140 | + } | | 141 | + }); | | 142 | + }); | | 143 | + | | 144 | + | | 145 | + }); | | 146 | + | | 147 | + return workspaceEdit; | | 148 | +} | 77 | 149 | export function getTextEdits(before: string, after: string): TextEdit[] { | 78 | 150 | let d = new dmp.diff_match_patch(); | 79 | 151 | let diffs = d.diff_main(before, after); | 80 | | - return getTextEditsInternal(before, diffs); | | 152 | + return getTextEditsInternal(before, diffs).map(edit => edit.apply()); | 81 | 153 | } | 82 | | -function getTextEditsInternal(before: string, diffs: [number, string][], startLine: number = 0): TextEdit[] { | | 154 | +function getTextEditsInternal(before: string, diffs: [number, string][], startLine: number = 0): Edit[] { | 83 | 155 | let line = startLine; | 84 | 156 | let character = 0; | 85 | 157 | if (line > 0) { | 86 | 158 | let beforeLines = <string[]>before.split(/\r?\n/g); | 87 | 159 | beforeLines.filter((l, i) => i < line).forEach(l => character += l.length + NEW_LINE_LENGTH); | 88 | 160 | } | 89 | | - let edits: TextEdit[] = []; | | 161 | + const edits: Edit[] = []; | 90 | 162 | let edit: Edit = null; | 91 | 163 |
| 92 | 164 | for (let i = 0; i < diffs.length; i++) { | @@ -128,15 +200,15 @@ function getTextEditsInternal(before: string, diffs: [number, string][], startLi | 128 | 200 |
| 129 | 201 | case dmp.DIFF_EQUAL: | 130 | 202 | if (edit != null) { | 131 | | - edits.push(edit.apply()); | | 203 | + edits.push(edit); | 132 | 204 | edit = null; | 133 | 205 | } | 134 | 206 | break; | 135 | 207 | } | 136 | 208 | } | 137 | 209 |
| 138 | 210 | if (edit != null) { | 139 | | - edits.push(edit.apply()); | | 211 | + edits.push(edit); | 140 | 212 | } | 141 | 213 |
| 142 | 214 | return edits; | |
Collapse file Copy file name to clipboardExpand all lines: src/client/common/telemetryContracts.ts +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -17,6 +17,7 @@ export namespace IDE { | 17 | 17 | export const Lint = 'LINTING'; | 18 | 18 | } | 19 | 19 | export namespace REFACTOR { | | 20 | + export const Rename = 'REFACTOR_RENAME'; | 20 | 21 | export const ExtractVariable = 'REFACTOR_EXTRACT_VAR'; | 21 | 22 | export const ExtractMethod = 'REFACTOR_EXTRACT_METHOD'; | 22 | 23 | } | |
Collapse file Copy file name to clipboardExpand all lines: src/client/extension.ts +7-6Lines changed: 7 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -61,16 +61,17 @@ export function activate(context: vscode.ExtensionContext) { | 61 | 61 | ] | 62 | 62 | }); | 63 | 63 |
| 64 | | - let renameProvider = new PythonRenameProvider(context); | 65 | | - context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, renameProvider)); | | 64 | + context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, new PythonRenameProvider())); | 66 | 65 | context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(context))); | 67 | | - context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, new PythonDefinitionProvider(context))); | 68 | | - context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context))); | | 66 | + const definitionProvider = new PythonDefinitionProvider(context); | | 67 | + const jediProx = definitionProvider.JediProxy; | | 68 | + context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, definitionProvider)); | | 69 | + context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context, jediProx))); | 69 | 70 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(context), '.')); | 70 | 71 |
| 71 | | - context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, new PythonSymbolProvider(context, renameProvider.JediProxy))); | | 72 | + context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, new PythonSymbolProvider(context, jediProx))); | 72 | 73 | if (pythonSettings.devOptions.indexOf('DISABLE_SIGNATURE') === -1) { | 73 | | - context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, renameProvider.JediProxy), '(', ',')); | | 74 | + context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, jediProx), '(', ',')); | 74 | 75 | } | 75 | 76 | const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel, pythonSettings, vscode.workspace.rootPath); | 76 | 77 | context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider(PYTHON, formatProvider)); | |
Collapse file Copy file name to clipboardExpand all lines: src/client/providers/definitionProvider.ts +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -7,6 +7,9 @@ import * as telemetryContracts from "../common/telemetryContracts"; | 7 | 7 |
| 8 | 8 | export class PythonDefinitionProvider implements vscode.DefinitionProvider { | 9 | 9 | private jediProxyHandler: proxy.JediProxyHandler<proxy.IDefinitionResult, vscode.Definition>; | | 10 | + public get JediProxy(): proxy.JediProxy { | | 11 | + return this.jediProxyHandler.JediProxy; | | 12 | + } | 10 | 13 |
| 11 | 14 | public constructor(context: vscode.ExtensionContext) { | 12 | 15 | this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonDefinitionProvider.parseData); | |
Collapse file Copy file name to clipboardExpand all lines: src/client/providers/referenceProvider.ts +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -8,8 +8,8 @@ import * as telemetryContracts from "../common/telemetryContracts"; | 8 | 8 | export class PythonReferenceProvider implements vscode.ReferenceProvider { | 9 | 9 | private jediProxyHandler: proxy.JediProxyHandler<proxy.IReferenceResult, vscode.Location[]>; | 10 | 10 |
| 11 | | - public constructor(context: vscode.ExtensionContext) { | 12 | | - this.jediProxyHandler = new proxy.JediProxyHandler(context, [], PythonReferenceProvider.parseData); | | 11 | + public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) { | | 12 | + this.jediProxyHandler = new proxy.JediProxyHandler(context, [], PythonReferenceProvider.parseData, jediProxy); | 13 | 13 | } | 14 | 14 | private static parseData(data: proxy.IReferenceResult): vscode.Location[] { | 15 | 15 | if (data && data.references.length > 0) { | |
Collapse file Copy file name to clipboard +33-57Lines changed: 33 additions & 57 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | 2 |
| 3 | 3 | import * as vscode from 'vscode'; | 4 | | -import * as proxy from './jediProxy'; | 5 | 4 | import * as telemetryContracts from "../common/telemetryContracts"; | 6 | | - | 7 | | -var _oldName = ""; | 8 | | -var _newName = ""; | | 5 | +import {RefactorProxy} from '../refactor/proxy'; | | 6 | +import {getWorkspaceEditsFromPatch, getTextEdits} from '../common/editor'; | | 7 | +import * as path from 'path'; | | 8 | +import {PythonSettings} from '../common/configSettings'; | | 9 | + | | 10 | +const pythonSettings = PythonSettings.getInstance(); | | 11 | +const EXTENSION_DIR = path.join(__dirname, '..', '..', '..'); | | 12 | +interface RenameResponse { | | 13 | + results: [{ diff: string }]; | | 14 | +} | 9 | 15 |
| 10 | 16 | export class PythonRenameProvider implements vscode.RenameProvider { | 11 | | - private jediProxyHandler: proxy.JediProxyHandler<proxy.IReferenceResult, vscode.WorkspaceEdit>; | 12 | | - public get JediProxy(): proxy.JediProxy { | 13 | | - return this.jediProxyHandler.JediProxy; | 14 | | - } | 15 | | - public constructor(context: vscode.ExtensionContext) { | 16 | | - this.jediProxyHandler = new proxy.JediProxyHandler(context, null, PythonRenameProvider.parseData); | 17 | | - } | 18 | | - private static parseData(data: proxy.IReferenceResult): vscode.WorkspaceEdit { | 19 | | - if (data && data.references.length > 0) { | 20 | | - var references = data.references.filter(ref => { | 21 | | - var relPath = vscode.workspace.asRelativePath(ref.fileName); | 22 | | - return !relPath.startsWith(".."); | 23 | | - }); | 24 | | - | 25 | | - var workSpaceEdit = new vscode.WorkspaceEdit(); | 26 | | - references.forEach(ref => { | 27 | | - var uri = vscode.Uri.file(ref.fileName); | 28 | | - var range = new vscode.Range(ref.lineIndex, ref.columnIndex, ref.lineIndex, ref.columnIndex + _oldName.length); | 29 | | - workSpaceEdit.replace(uri, range, _newName); | 30 | | - }); | 31 | | - return workSpaceEdit; | 32 | | - } | 33 | | - return; | 34 | | - } | 35 | 17 | public provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Thenable<vscode.WorkspaceEdit> { | 36 | 18 | return vscode.workspace.saveAll(false).then(() => { | 37 | 19 | return this.doRename(document, position, newName, token); | 38 | 20 | }); | 39 | 21 | } | 40 | 22 |
| 41 | 23 | private doRename(document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Thenable<vscode.WorkspaceEdit> { | 42 | | - return new Promise<vscode.WorkspaceEdit>((resolve, reject) => { | 43 | | - var filename = document.fileName; | 44 | | - if (document.lineAt(position.line).text.match(/^\s*\/\//)) { | 45 | | - return resolve(); | 46 | | - } | 47 | | - if (position.character <= 0) { | 48 | | - return resolve(); | 49 | | - } | 50 | | - | 51 | | - var source = document.getText(); | 52 | | - var range = document.getWordRangeAtPosition(position); | 53 | | - if (range == undefined || range == null || range.isEmpty) { | 54 | | - return resolve(); | 55 | | - } | 56 | | - _oldName = document.getText(range); | 57 | | - _newName = newName; | 58 | | - if (_oldName === newName) { | 59 | | - return resolve(); | 60 | | - } | | 24 | + var filename = document.fileName; | | 25 | + if (document.lineAt(position.line).text.match(/^\s*\/\//)) { | | 26 | + return; | | 27 | + } | | 28 | + if (position.character <= 0) { | | 29 | + return; | | 30 | + } | 61 | 31 |
| 62 | | - var columnIndex = range.isEmpty ? position.character : range.end.character; | 63 | | - var cmd: proxy.ICommand<proxy.IReferenceResult> = { | 64 | | - telemetryEvent: telemetryContracts.IDE.Rename, | 65 | | - command: proxy.CommandType.Usages, | 66 | | - fileName: filename, | 67 | | - columnIndex: columnIndex, | 68 | | - lineIndex: position.line, | 69 | | - source: source | 70 | | - }; | | 32 | + var source = document.getText(); | | 33 | + var range = document.getWordRangeAtPosition(position); | | 34 | + if (range == undefined || range == null || range.isEmpty) { | | 35 | + return; | | 36 | + } | | 37 | + const oldName = document.getText(range); | | 38 | + if (oldName === newName) { | | 39 | + return; | | 40 | + } | 71 | 41 |
| 72 | | - this.jediProxyHandler.sendCommand(cmd, resolve, token); | | 42 | + let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, vscode.workspace.rootPath); | | 43 | + return new Promise<vscode.WorkspaceEdit>(resolve => { | | 44 | + proxy.rename<RenameResponse>(document, newName, document.uri.fsPath, range).then(response => { | | 45 | + //return response.results[0].diff; | | 46 | + const workspaceEdit = getWorkspaceEditsFromPatch(response.results.map(fileChanges => fileChanges.diff)); | | 47 | + resolve(workspaceEdit); | | 48 | + }); | 73 | 49 | }); | 74 | 50 | } | 75 | 51 | } |
Collapse file Copy file name to clipboardExpand all lines: src/client/refactor/proxy.ts +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -35,6 +35,10 @@ export class RefactorProxy extends vscode.Disposable { | 35 | 35 | } | 36 | 36 | this._process = null; | 37 | 37 | } | | 38 | + rename<T>(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range): Promise<T> { | | 39 | + let command = { "lookup": "rename", "file": filePath, "start": document.offsetAt(range.start).toString(), "id": "1", "name": name }; | | 40 | + return this.sendCommand<T>(JSON.stringify(command), REFACTOR.Rename); | | 41 | + } | 38 | 42 | extractVariable<T>(document: vscode.TextDocument, name: string, filePath: string, range: vscode.Range): Promise<T> { | 39 | 43 | let command = { "lookup": "extract_variable", "file": filePath, "start": document.offsetAt(range.start).toString(), "end": document.offsetAt(range.end).toString(), "id": "1", "name": name }; | 40 | 44 | return this.sendCommand<T>(JSON.stringify(command), REFACTOR.ExtractVariable); | |
|