Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-eslint / server / util.ts
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'
6
7 interface Change {
8   start: number
9   end: number
10   newText: string
11 }
12
13 const enum CharCode {
14   /**
15    * The `\` character.
16    */
17   Backslash = 92
18 }
19
20 /**
21  * Check if the path follows this pattern: `\\hostname\sharename`.
22  *
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
25  * always false.
26  */
27 export function isUNC(path: string): boolean {
28   if (process.platform !== 'win32') {
29     // UNC is a windows concept
30     return false
31   }
32
33   if (!path || path.length < 5) {
34     // at least \\a\b
35     return false
36   }
37
38   let code = path.charCodeAt(0)
39   if (code !== CharCode.Backslash) {
40     return false
41   }
42   code = path.charCodeAt(1)
43   if (code !== CharCode.Backslash) {
44     return false
45   }
46   let pos = 2
47   let start = pos
48   for (; pos < path.length; pos++) {
49     code = path.charCodeAt(pos)
50     if (code === CharCode.Backslash) {
51       break
52     }
53   }
54   if (start === pos) {
55     return false
56   }
57   code = path.charCodeAt(pos + 1)
58   if (isNaN(code) || code === CharCode.Backslash) {
59     return false
60   }
61   return true
62 }
63
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)
71   } else {
72     return result
73   }
74 }
75
76 export function getFilePath(documentOrUri: string | TextDocument): string {
77   if (!documentOrUri) {
78     return undefined
79   }
80   let uri = Is.string(documentOrUri)
81     ? URI.parse(documentOrUri)
82     : URI.parse(documentOrUri.uri)
83   if (uri.scheme !== 'file') {
84     return undefined
85   }
86   return getFileSystemPath(uri)
87 }
88
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;
95   }
96   const newOptions: CLIOptions = {...settings.options, fix: fixRule}
97   return executeInWorkspaceDirectory(document, settings, newOptions, (filename: string, options: CLIOptions) => {
98     if (!settings.validate) {
99       return []
100     }
101     const engine = new settings.library.CLIEngine(options)
102     const res = engine.executeOnText(content, filename)
103     if (!res.results.length) return []
104
105     const { output } = res.results[0]
106     if (output == null) return []
107
108     const change = getChange(content, output)
109     return [{
110       range: {
111         start: document.positionAt(change.start),
112         end: document.positionAt(change.end)
113       },
114       newText: change.newText
115     }]
116   })
117 }
118
119 export function getChange(oldStr: string, newStr: string): Change {
120   let result = fastDiff(oldStr, newStr, 1)
121   let curr = 0
122   let start = -1
123   let end = -1
124   let newText = ''
125   let remain = ''
126   for (let item of result) {
127     let [t, str] = item
128     // equal
129     if (t == 0) {
130       curr = curr + str.length
131       if (start != -1) remain = remain + str
132     } else {
133       if (start == -1) start = curr
134       if (t == 1) {
135         newText = newText + remain + str
136         end = curr
137       } else {
138         newText = newText + remain
139         end = curr + str.length
140       }
141       remain = ''
142       if (t == -1) curr = curr + str.length
143     }
144   }
145   return { start, end, newText }
146 }
147
148 export function executeInWorkspaceDirectory(document: TextDocument, settings: TextDocumentSettings, newOptions: CLIOptions, callback: Function): TextEdit[] {
149   const filename = getFilePath(document)
150   const cwd = process.cwd()
151   try {
152     if (filename) {
153       if (settings.workingDirectory) {
154         newOptions.cwd = settings.workingDirectory.directory
155         if (settings.workingDirectory.changeProcessCWD) {
156           process.chdir(settings.workingDirectory.directory)
157         }
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)
164         }
165       } else if (!settings.workspaceFolder && !isUNC(filename)) {
166         const directory = path.dirname(filename)
167         if (directory && path.isAbsolute(directory)) newOptions.cwd = directory
168       }
169     }
170
171     return callback(filename, newOptions)
172   } finally {
173     if (cwd !== process.cwd()) process.chdir(cwd)
174   }
175 }