Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
徽霖
Vscode
提交
54f1a180
V
Vscode
项目概览
徽霖
/
Vscode
通知
9
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
Vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
54f1a180
编写于
3月 20, 2018
作者:
P
Pine
提交者:
GitHub
3月 20, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #45788 from Microsoft/octref/pathCompletion
path completion for css. fix #45235
上级
2f5061ff
d2b9a7cc
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
256 addition
and
7 deletion
+256
-7
extensions/css/.vscode/launch.json
extensions/css/.vscode/launch.json
+8
-1
extensions/css/server/src/cssServerMain.ts
extensions/css/server/src/cssServerMain.ts
+15
-4
extensions/css/server/src/pathCompletion.ts
extensions/css/server/src/pathCompletion.ts
+119
-0
extensions/css/server/src/test/completion.test.ts
extensions/css/server/src/test/completion.test.ts
+81
-0
extensions/css/server/src/test/emmet.test.ts
extensions/css/server/src/test/emmet.test.ts
+1
-1
extensions/css/server/src/utils/strings.ts
extensions/css/server/src/utils/strings.ts
+19
-0
extensions/css/server/test/pathCompletionFixtures/about/about.css
...ns/css/server/test/pathCompletionFixtures/about/about.css
+4
-0
extensions/css/server/test/pathCompletionFixtures/about/about.html
...s/css/server/test/pathCompletionFixtures/about/about.html
+0
-0
extensions/css/server/test/pathCompletionFixtures/index.html
extensions/css/server/test/pathCompletionFixtures/index.html
+0
-0
extensions/css/server/test/pathCompletionFixtures/src/feature.js
...ons/css/server/test/pathCompletionFixtures/src/feature.js
+4
-0
extensions/css/server/test/pathCompletionFixtures/src/test.js
...nsions/css/server/test/pathCompletionFixtures/src/test.js
+4
-0
extensions/html-language-features/server/src/test/completions.test.ts
...tml-language-features/server/src/test/completions.test.ts
+1
-1
未找到文件。
extensions/css/.vscode/launch.json
浏览文件 @
54f1a180
{
"version"
:
"0.2.0"
,
"compounds"
:
[
{
"name"
:
"Debug Extension and Language Server"
,
"configurations"
:
[
"Launch Extension"
,
"Attach Language Server"
]
}
],
"configurations"
:
[
{
"name"
:
"Launch Extension"
,
...
...
@@ -32,7 +38,8 @@
"protocol"
:
"inspector"
,
"port"
:
6044
,
"sourceMaps"
:
true
,
"outFiles"
:
[
"${workspaceFolder}/server/out/**/*.js"
]
"outFiles"
:
[
"${workspaceFolder}/server/out/**/*.js"
],
"restart"
:
true
}
]
}
\ No newline at end of file
extensions/css/server/src/cssServerMain.ts
浏览文件 @
54f1a180
...
...
@@ -9,12 +9,13 @@ import {
ConfigurationRequest
,
WorkspaceFolder
,
DocumentColorRequest
,
ColorPresentationRequest
}
from
'
vscode-languageserver
'
;
import
{
TextDocument
}
from
'
vscode-languageserver-types
'
;
import
{
TextDocument
,
CompletionList
}
from
'
vscode-languageserver-types
'
;
import
{
getCSSLanguageService
,
getSCSSLanguageService
,
getLESSLanguageService
,
LanguageSettings
,
LanguageService
,
Stylesheet
}
from
'
vscode-css-languageservice
'
;
import
{
getLanguageModelCache
}
from
'
./languageModelCache
'
;
import
{
formatError
,
runSafe
}
from
'
./utils/errors
'
;
import
uri
from
'
vscode-uri
'
;
import
URI
from
'
vscode-uri
'
;
import
{
getPathCompletionParticipant
}
from
'
./pathCompletion
'
;
export
interface
Settings
{
css
:
LanguageSettings
;
...
...
@@ -57,7 +58,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
if
(
!
Array
.
isArray
(
workspaceFolders
))
{
workspaceFolders
=
[];
if
(
params
.
rootPath
)
{
workspaceFolders
.
push
({
name
:
''
,
uri
:
uri
.
file
(
params
.
rootPath
).
toString
()
});
workspaceFolders
.
push
({
name
:
''
,
uri
:
URI
.
file
(
params
.
rootPath
).
toString
()
});
}
}
...
...
@@ -181,7 +182,17 @@ function validateTextDocument(textDocument: TextDocument): void {
connection
.
onCompletion
(
textDocumentPosition
=>
{
return
runSafe
(()
=>
{
let
document
=
documents
.
get
(
textDocumentPosition
.
textDocument
.
uri
);
return
getLanguageService
(
document
).
doComplete
(
document
,
textDocumentPosition
.
position
,
stylesheets
.
get
(
document
))
!
;
/* TODO: remove ! once LS has null annotations */
const
cssLS
=
getLanguageService
(
document
);
const
pathCompletionList
:
CompletionList
=
{
isIncomplete
:
false
,
items
:
[]
};
cssLS
.
setCompletionParticipants
([
getPathCompletionParticipant
(
document
,
workspaceFolders
,
pathCompletionList
)]);
const
result
=
cssLS
.
doComplete
(
document
,
textDocumentPosition
.
position
,
stylesheets
.
get
(
document
))
!
;
/* TODO: remove ! once LS has null annotations */
return
{
isIncomplete
:
result
.
isIncomplete
,
items
:
[...
pathCompletionList
.
items
,
...
result
.
items
]
};
},
null
,
`Error while computing completions for
${
textDocumentPosition
.
textDocument
.
uri
}
`
);
});
...
...
extensions/css/server/src/pathCompletion.ts
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
*
as
path
from
'
path
'
;
import
*
as
fs
from
'
fs
'
;
import
URI
from
'
vscode-uri
'
;
import
{
TextDocument
,
CompletionList
,
CompletionItemKind
,
CompletionItem
,
TextEdit
,
Range
,
Position
}
from
'
vscode-languageserver-types
'
;
import
{
WorkspaceFolder
}
from
'
vscode-languageserver
'
;
import
{
ICompletionParticipant
}
from
'
vscode-css-languageservice
'
;
import
{
startsWith
}
from
'
./utils/strings
'
;
export
function
getPathCompletionParticipant
(
document
:
TextDocument
,
workspaceFolders
:
WorkspaceFolder
[]
|
undefined
,
result
:
CompletionList
):
ICompletionParticipant
{
return
{
onURILiteralValue
:
(
context
:
{
uriValue
:
string
,
position
:
Position
,
range
:
Range
;
})
=>
{
if
(
!
workspaceFolders
||
workspaceFolders
.
length
===
0
)
{
return
;
}
const
workspaceRoot
=
resolveWorkspaceRoot
(
document
,
workspaceFolders
);
// Handle quoted values
let
uriValue
=
context
.
uriValue
;
let
range
=
context
.
range
;
if
(
startsWith
(
uriValue
,
`'`
)
||
startsWith
(
uriValue
,
`"`
))
{
uriValue
=
uriValue
.
slice
(
1
,
-
1
);
range
=
getRangeWithoutQuotes
(
range
);
}
const
suggestions
=
providePathSuggestions
(
uriValue
,
range
,
URI
.
parse
(
document
.
uri
).
fsPath
,
workspaceRoot
);
result
.
items
=
[...
suggestions
,
...
result
.
items
];
}
};
}
export
function
providePathSuggestions
(
value
:
string
,
range
:
Range
,
activeDocFsPath
:
string
,
root
?:
string
):
CompletionItem
[]
{
if
(
startsWith
(
value
,
'
/
'
)
&&
!
root
)
{
return
[];
}
let
replaceRange
:
Range
;
const
lastIndexOfSlash
=
value
.
lastIndexOf
(
'
/
'
);
if
(
lastIndexOfSlash
===
-
1
)
{
replaceRange
=
getFullReplaceRange
(
range
);
}
else
{
const
valueAfterLastSlash
=
value
.
slice
(
lastIndexOfSlash
+
1
);
replaceRange
=
getReplaceRange
(
range
,
valueAfterLastSlash
);
}
let
parentDir
:
string
;
if
(
lastIndexOfSlash
===
-
1
)
{
parentDir
=
path
.
resolve
(
root
);
}
else
{
const
valueBeforeLastSlash
=
value
.
slice
(
0
,
lastIndexOfSlash
+
1
);
parentDir
=
startsWith
(
value
,
'
/
'
)
?
path
.
resolve
(
root
,
'
.
'
+
valueBeforeLastSlash
)
:
path
.
resolve
(
activeDocFsPath
,
'
..
'
,
valueBeforeLastSlash
);
}
try
{
return
fs
.
readdirSync
(
parentDir
).
map
(
f
=>
{
if
(
isDir
(
path
.
resolve
(
parentDir
,
f
)))
{
return
{
label
:
f
+
'
/
'
,
kind
:
CompletionItemKind
.
Folder
,
textEdit
:
TextEdit
.
replace
(
replaceRange
,
f
+
'
/
'
),
command
:
{
title
:
'
Suggest
'
,
command
:
'
editor.action.triggerSuggest
'
}
};
}
else
{
return
{
label
:
f
,
kind
:
CompletionItemKind
.
File
,
textEdit
:
TextEdit
.
replace
(
replaceRange
,
f
)
};
}
});
}
catch
(
e
)
{
return
[];
}
}
const
isDir
=
(
p
:
string
)
=>
{
return
fs
.
statSync
(
p
).
isDirectory
();
};
function
resolveWorkspaceRoot
(
activeDoc
:
TextDocument
,
workspaceFolders
:
WorkspaceFolder
[]):
string
|
undefined
{
for
(
let
i
=
0
;
i
<
workspaceFolders
.
length
;
i
++
)
{
if
(
startsWith
(
activeDoc
.
uri
,
workspaceFolders
[
i
].
uri
))
{
return
path
.
resolve
(
URI
.
parse
(
workspaceFolders
[
i
].
uri
).
fsPath
);
}
}
}
function
getFullReplaceRange
(
valueRange
:
Range
)
{
const
start
=
Position
.
create
(
valueRange
.
end
.
line
,
valueRange
.
start
.
character
);
const
end
=
Position
.
create
(
valueRange
.
end
.
line
,
valueRange
.
end
.
character
);
return
Range
.
create
(
start
,
end
);
}
function
getReplaceRange
(
valueRange
:
Range
,
valueAfterLastSlash
:
string
)
{
const
start
=
Position
.
create
(
valueRange
.
end
.
line
,
valueRange
.
end
.
character
-
valueAfterLastSlash
.
length
);
const
end
=
Position
.
create
(
valueRange
.
end
.
line
,
valueRange
.
end
.
character
);
return
Range
.
create
(
start
,
end
);
}
function
getRangeWithoutQuotes
(
range
:
Range
)
{
const
start
=
Position
.
create
(
range
.
start
.
line
,
range
.
start
.
character
+
1
);
const
end
=
Position
.
create
(
range
.
end
.
line
,
range
.
end
.
character
-
1
);
return
Range
.
create
(
start
,
end
);
}
extensions/css/server/src/test/completion.test.ts
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
'
mocha
'
;
import
*
as
assert
from
'
assert
'
;
import
*
as
path
from
'
path
'
;
import
Uri
from
'
vscode-uri
'
;
import
{
TextDocument
,
CompletionList
}
from
'
vscode-languageserver-types
'
;
import
{
WorkspaceFolder
}
from
'
vscode-languageserver-protocol
'
;
import
{
getPathCompletionParticipant
}
from
'
../pathCompletion
'
;
import
{
getCSSLanguageService
}
from
'
vscode-css-languageservice
'
;
export
interface
ItemDescription
{
label
:
string
;
resultText
?:
string
;
}
suite
(
'
Completions
'
,
()
=>
{
const
cssLanguageService
=
getCSSLanguageService
();
let
assertCompletion
=
function
(
completions
:
CompletionList
,
expected
:
ItemDescription
,
document
:
TextDocument
,
offset
:
number
)
{
let
matches
=
completions
.
items
.
filter
(
completion
=>
{
return
completion
.
label
===
expected
.
label
;
});
assert
.
equal
(
matches
.
length
,
1
,
`
${
expected
.
label
}
should only existing once: Actual:
${
completions
.
items
.
map
(
c
=>
c
.
label
).
join
(
'
,
'
)}
`
);
let
match
=
matches
[
0
];
if
(
expected
.
resultText
&&
match
.
textEdit
)
{
assert
.
equal
(
TextDocument
.
applyEdits
(
document
,
[
match
.
textEdit
]),
expected
.
resultText
);
}
};
function
assertCompletions
(
value
:
string
,
expected
:
{
count
?:
number
,
items
?:
ItemDescription
[]
},
testUri
:
string
,
workspaceFolders
?:
WorkspaceFolder
[]):
void
{
const
offset
=
value
.
indexOf
(
'
|
'
);
value
=
value
.
substr
(
0
,
offset
)
+
value
.
substr
(
offset
+
1
);
const
document
=
TextDocument
.
create
(
testUri
,
'
css
'
,
0
,
value
);
const
position
=
document
.
positionAt
(
offset
);
if
(
!
workspaceFolders
)
{
workspaceFolders
=
[{
name
:
'
x
'
,
uri
:
path
.
dirname
(
testUri
)
}];
}
let
participantResult
=
CompletionList
.
create
([]);
cssLanguageService
.
setCompletionParticipants
([
getPathCompletionParticipant
(
document
,
workspaceFolders
,
participantResult
)]);
const
stylesheet
=
cssLanguageService
.
parseStylesheet
(
document
);
let
list
=
cssLanguageService
.
doComplete
!
(
document
,
position
,
stylesheet
);
list
.
items
=
list
.
items
.
concat
(
participantResult
.
items
);
if
(
expected
.
count
)
{
assert
.
equal
(
list
.
items
.
length
,
expected
.
count
);
}
if
(
expected
.
items
)
{
for
(
let
item
of
expected
.
items
)
{
assertCompletion
(
list
,
item
,
document
,
offset
);
}
}
}
test
(
'
CSS Path completion
'
,
function
()
{
let
testUri
=
Uri
.
file
(
path
.
resolve
(
__dirname
,
'
../../test/pathCompletionFixtures/about/about.css
'
)).
fsPath
;
assertCompletions
(
'
html { background-image: url("./|")
'
,
{
items
:
[
{
label
:
'
about.html
'
,
resultText
:
'
html { background-image: url("./about.html")
'
}
]
},
testUri
);
assertCompletions
(
`html { background-image: url('../|')`
,
{
items
:
[
{
label
:
'
about/
'
,
resultText
:
`html { background-image: url('../about/')`
},
{
label
:
'
index.html
'
,
resultText
:
`html { background-image: url('../index.html')`
},
{
label
:
'
src/
'
,
resultText
:
`html { background-image: url('../src/')`
}
]
},
testUri
);
});
});
\ No newline at end of file
extensions/css/server/src/test/emmet.test.ts
浏览文件 @
54f1a180
...
...
@@ -6,7 +6,7 @@
import
'
mocha
'
;
import
*
as
assert
from
'
assert
'
;
import
{
getCSSLanguageService
,
getSCSSLanguageService
}
from
'
vscode-css-languageservice
'
;
import
{
getCSSLanguageService
,
getSCSSLanguageService
}
from
'
vscode-css-languageservice
/lib/umd/cssLanguageService
'
;
import
{
TextDocument
,
CompletionList
}
from
'
vscode-languageserver-types
'
;
import
{
getEmmetCompletionParticipants
}
from
'
vscode-emmet-helper
'
;
...
...
extensions/css/server/src/utils/strings.ts
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
export
function
startsWith
(
haystack
:
string
,
needle
:
string
):
boolean
{
if
(
haystack
.
length
<
needle
.
length
)
{
return
false
;
}
for
(
let
i
=
0
;
i
<
needle
.
length
;
i
++
)
{
if
(
haystack
[
i
]
!==
needle
[
i
])
{
return
false
;
}
}
return
true
;
}
extensions/css/server/test/pathCompletionFixtures/about/about.css
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
\ No newline at end of file
extensions/css/server/test/pathCompletionFixtures/about/about.html
0 → 100644
浏览文件 @
54f1a180
extensions/css/server/test/pathCompletionFixtures/index.html
0 → 100644
浏览文件 @
54f1a180
extensions/css/server/test/pathCompletionFixtures/src/feature.js
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
\ No newline at end of file
extensions/css/server/test/pathCompletionFixtures/src/test.js
0 → 100644
浏览文件 @
54f1a180
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
\ No newline at end of file
extensions/html-language-features/server/src/test/completions.test.ts
浏览文件 @
54f1a180
...
...
@@ -8,7 +8,7 @@ import 'mocha';
import
*
as
assert
from
'
assert
'
;
import
*
as
path
from
'
path
'
;
import
Uri
from
'
vscode-uri
'
;
import
{
TextDocument
,
CompletionList
,
CompletionItemKind
,
}
from
'
vscode-languageserver-types
'
;
import
{
TextDocument
,
CompletionList
,
CompletionItemKind
}
from
'
vscode-languageserver-types
'
;
import
{
getLanguageModes
}
from
'
../modes/languageModes
'
;
import
{
getPathCompletionParticipant
}
from
'
../modes/pathCompletion
'
;
import
{
WorkspaceFolder
}
from
'
vscode-languageserver
'
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录