1 import * as path from 'path'
2 import fastDiff from 'fast-diff'
3 import { TextDocument, TextEdit } from 'vscode-languageserver'
4 import { CLIOptions, ESLintProblem, Is, TextDocumentSettings } from './types'
5 import { URI } from 'vscode-uri'
21 * Check if the path follows this pattern: `\\hostname\sharename`.
23 * @see https://msdn.microsoft.com/en-us/library/gg465305.aspx
24 * @return A boolean indication if the path is a UNC path, on none-windows
27 export function isUNC(path: string): boolean {
28 if (process.platform !== 'win32') {
29 // UNC is a windows concept
33 if (!path || path.length < 5) {
38 let code = path.charCodeAt(0)
39 if (code !== CharCode.Backslash) {
42 code = path.charCodeAt(1)
43 if (code !== CharCode.Backslash) {
48 for (; pos < path.length; pos++) {
49 code = path.charCodeAt(pos)
50 if (code === CharCode.Backslash) {
57 code = path.charCodeAt(pos + 1)
58 if (isNaN(code) || code === CharCode.Backslash) {
64 function getFileSystemPath(uri: URI): string {
65 let result = uri.fsPath
66 if (process.platform === 'win32' && result.length >= 2 && result[1] === ':') {
67 // Node by default uses an upper case drive letter and ESLint uses
68 // === to compare pathes which results in the equal check failing
69 // if the drive letter is lower case in th URI. Ensure upper case.
70 return result[0].toUpperCase() + result.substr(1)
76 export function getFilePath(documentOrUri: string | TextDocument): string {
80 let uri = Is.string(documentOrUri)
81 ? URI.parse(documentOrUri)
82 : URI.parse(documentOrUri.uri)
83 if (uri.scheme !== 'file') {
86 return getFileSystemPath(uri)
89 export function getAllFixEdits(document: TextDocument, settings: TextDocumentSettings): TextEdit[] {
90 const uri = URI.parse(document.uri)
91 if (uri.scheme != 'file') return []
92 const content = document.getText()
93 const fixRule = (problem: ESLintProblem): boolean => {
94 return settings.autoFixSkipRules.indexOf(problem.ruleId) === -1;
96 const newOptions: CLIOptions = {...settings.options, fix: fixRule}
97 return executeInWorkspaceDirectory(document, settings, newOptions, (filename: string, options: CLIOptions) => {
98 if (!settings.validate) {
101 const engine = new settings.library.CLIEngine(options)
102 const res = engine.executeOnText(content, filename)
103 if (!res.results.length) return []
105 const { output } = res.results[0]
106 if (output == null) return []
108 const change = getChange(content, output)
111 start: document.positionAt(change.start),
112 end: document.positionAt(change.end)
114 newText: change.newText
119 export function getChange(oldStr: string, newStr: string): Change {
120 let result = fastDiff(oldStr, newStr, 1)
126 for (let item of result) {
130 curr = curr + str.length
131 if (start != -1) remain = remain + str
133 if (start == -1) start = curr
135 newText = newText + remain + str
138 newText = newText + remain
139 end = curr + str.length
142 if (t == -1) curr = curr + str.length
145 return { start, end, newText }
148 export function executeInWorkspaceDirectory(document: TextDocument, settings: TextDocumentSettings, newOptions: CLIOptions, callback: Function): TextEdit[] {
149 const filename = getFilePath(document)
150 const cwd = process.cwd()
153 if (settings.workingDirectory) {
154 newOptions.cwd = settings.workingDirectory.directory
155 if (settings.workingDirectory.changeProcessCWD) {
156 process.chdir(settings.workingDirectory.directory)
158 } else if (settings.workspaceFolder) {
159 const workspaceFolderUri = URI.parse(settings.workspaceFolder.uri)
160 if (workspaceFolderUri.scheme === 'file') {
161 const fsPath = getFileSystemPath(workspaceFolderUri)
162 newOptions.cwd = fsPath
163 process.chdir(fsPath)
165 } else if (!settings.workspaceFolder && !isUNC(filename)) {
166 const directory = path.dirname(filename)
167 if (directory && path.isAbsolute(directory)) newOptions.cwd = directory
171 return callback(filename, newOptions)
173 if (cwd !== process.cwd()) process.chdir(cwd)