Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
df2d1d26
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
df2d1d26
编写于
1月 13, 2017
作者:
I
Isidor Nikolic
提交者:
GitHub
1月 13, 2017
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #16129 from nojvek/master
inline values as decorators when debugging
上级
04fcbabb
40779477
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
429 addition
and
1 deletion
+429
-1
src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts
...h/parts/debug/electron-browser/debugEditorContribution.ts
+49
-1
src/vs/workbench/parts/debug/electron-browser/debugInlineDecorators.ts
...nch/parts/debug/electron-browser/debugInlineDecorators.ts
+166
-0
src/vs/workbench/parts/debug/test/electron-browser/debugInlineDecorators.test.ts
...debug/test/electron-browser/debugInlineDecorators.test.ts
+214
-0
未找到文件。
src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts
浏览文件 @
df2d1d26
...
...
@@ -14,9 +14,11 @@ import { visit } from 'vs/base/common/json';
import
{
IAction
,
Action
}
from
'
vs/base/common/actions
'
;
import
{
KeyCode
}
from
'
vs/base/common/keyCodes
'
;
import
{
IKeyboardEvent
}
from
'
vs/base/browser/keyboardEvent
'
;
import
{
IStringDictionary
}
from
'
vs/base/common/collections
'
;
import
{
ICodeEditor
,
IEditorMouseEvent
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
editorContribution
}
from
'
vs/editor/browser/editorBrowserExtensions
'
;
import
{
IModelDecorationOptions
,
MouseTargetType
,
IModelDeltaDecoration
,
TrackedRangeStickiness
,
IPosition
}
from
'
vs/editor/common/editorCommon
'
;
import
{
IRange
,
IModelDecorationOptions
,
MouseTargetType
,
IModelDeltaDecoration
,
TrackedRangeStickiness
,
IPosition
}
from
'
vs/editor/common/editorCommon
'
;
import
{
ICodeEditorService
}
from
'
vs/editor/common/services/codeEditorService
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
Selection
}
from
'
vs/editor/common/core/selection
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
...
...
@@ -29,9 +31,12 @@ import { RemoveBreakpointAction, EditConditionalBreakpointAction, EnableBreakpoi
import
{
IDebugEditorContribution
,
IDebugService
,
State
,
IBreakpoint
,
EDITOR_CONTRIBUTION_ID
,
CONTEXT_BREAKPOINT_WIDGET_VISIBLE
,
IStackFrame
}
from
'
vs/workbench/parts/debug/common/debug
'
;
import
{
BreakpointWidget
}
from
'
vs/workbench/parts/debug/browser/breakpointWidget
'
;
import
{
FloatingClickWidget
}
from
'
vs/workbench/parts/preferences/browser/preferencesWidgets
'
;
import
{
getNameValueMapFromScopeChildren
,
getDecorators
,
getEditorWordRangeMap
}
from
'
vs/workbench/parts/debug/electron-browser/debugInlineDecorators
'
;
const
HOVER_DELAY
=
300
;
const
LAUNCH_JSON_REGEX
=
/launch
\.
json$/
;
const
REMOVE_DECORATORS_DEBOUNCE_INTERVAL
=
100
;
// If we receive a break in this interval, don't reset decorators as it causes a UI flash.
const
INLINE_DECORATOR_KEY
=
'
inlineDecorator
'
;
@
editorContribution
export
class
DebugEditorContribution
implements
IDebugEditorContribution
{
...
...
@@ -45,6 +50,8 @@ export class DebugEditorContribution implements IDebugEditorContribution {
private
breakpointHintDecoration
:
string
[];
private
breakpointWidget
:
BreakpointWidget
;
private
breakpointWidgetVisible
:
IContextKey
<
boolean
>
;
private
removeDecorationsTimeoutId
=
0
;
private
editorModelWordRangeMap
:
IStringDictionary
<
IRange
[]
>
;
private
configurationWidget
:
FloatingClickWidget
;
...
...
@@ -55,6 +62,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
@
IInstantiationService
private
instantiationService
:
IInstantiationService
,
@
IContextKeyService
contextKeyService
:
IContextKeyService
,
@
ICommandService
private
commandService
:
ICommandService
,
@
ICodeEditorService
private
codeEditorService
:
ICodeEditorService
,
@
ITelemetryService
private
telemetryService
:
ITelemetryService
)
{
this
.
breakpointHintDecoration
=
[];
...
...
@@ -65,6 +73,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this
.
registerListeners
();
this
.
breakpointWidgetVisible
=
CONTEXT_BREAKPOINT_WIDGET_VISIBLE
.
bindTo
(
contextKeyService
);
this
.
updateConfigurationWidgetVisibility
();
this
.
codeEditorService
.
registerDecorationType
(
INLINE_DECORATOR_KEY
,
{});
}
private
getContextMenuActions
(
breakpoint
:
IBreakpoint
,
uri
:
uri
,
lineNumber
:
number
):
TPromise
<
IAction
[]
>
{
...
...
@@ -200,6 +209,45 @@ export class DebugEditorContribution implements IDebugEditorContribution {
this
.
editor
.
updateOptions
({
hover
:
true
});
this
.
hideHoverWidget
();
}
this
.
updateInlineDecorators
(
sf
);
}
private
updateInlineDecorators
(
stackFrame
:
IStackFrame
):
void
{
// Since step over, step out is a fast continue + break. Continue clears stack.
// This means we'll get a null stackFrame followed quickly by a valid stackFrame.
// Removing all decorators and adding them again causes a noticeable UI flash due to relayout and paint.
// We want to only remove inline decorations if a null stackFrame isn't followed by a valid stackFrame in a short interval.
clearTimeout
(
this
.
removeDecorationsTimeoutId
);
if
(
!
stackFrame
)
{
this
.
removeDecorationsTimeoutId
=
setTimeout
(()
=>
{
this
.
editor
.
removeDecorations
(
INLINE_DECORATOR_KEY
);
this
.
editorModelWordRangeMap
=
null
;
},
REMOVE_DECORATORS_DEBOUNCE_INTERVAL
);
return
;
}
// URI has changed, invalidate the editorWordRangeMap so its re-computed for the current model
if
(
stackFrame
.
source
.
uri
.
toString
()
!==
this
.
editor
.
getModel
().
uri
.
toString
())
{
this
.
editorModelWordRangeMap
=
null
;
}
stackFrame
.
getScopes
()
// Get all top level children in the scope chain
.
then
(
scopes
=>
TPromise
.
join
(
scopes
.
map
(
scope
=>
scope
.
getChildren
())))
.
then
(
children
=>
{
const
editorModel
=
this
.
editor
.
getModel
();
// Compute name-value map for all variables in scope chain
const
expressions
=
[].
concat
.
apply
([],
children
);
const
nameValueMap
=
getNameValueMapFromScopeChildren
(
expressions
);
// Build wordRangeMap if not already computed for the editor model
if
(
!
this
.
editorModelWordRangeMap
)
{
this
.
editorModelWordRangeMap
=
getEditorWordRangeMap
(
editorModel
);
}
// Compute decorators from nameValueMap and wordRangeMap and apply to editor
const
decorators
=
getDecorators
(
nameValueMap
,
this
.
editorModelWordRangeMap
,
editorModel
.
getLinesContent
());
this
.
editor
.
setDecorations
(
INLINE_DECORATOR_KEY
,
decorators
);
});
}
private
hideHoverWidget
():
void
{
...
...
src/vs/workbench/parts/debug/electron-browser/debugInlineDecorators.ts
0 → 100644
浏览文件 @
df2d1d26
/*---------------------------------------------------------------------------------------------
* 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
{
IStringDictionary
}
from
'
vs/base/common/collections
'
;
import
{
IDecorationOptions
,
IRange
,
IModel
}
from
'
vs/editor/common/editorCommon
'
;
import
{
StandardTokenType
}
from
'
vs/editor/common/modes
'
;
import
{
IExpression
}
from
'
vs/workbench/parts/debug/common/debug
'
;
export
const
MAX_INLINE_VALUE_LENGTH
=
50
;
// Max string length of each inline 'x = y' string. If exceeded ... is added
export
const
MAX_INLINE_DECORATOR_LENGTH
=
150
;
// Max string length of each inline decorator when debugging. If exceeded ... is added
export
const
MAX_NUM_INLINE_VALUES
=
100
;
// JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons
export
const
MAX_TOKENIZATION_LINE_LEN
=
500
;
// If line is too long, then inline values for the line are skipped
export
const
ELLIPSES
=
'
…
'
;
// LanguageConfigurationRegistry.getWordDefinition() return regexes that allow spaces and punctuation characters for languages like python
// Using that approach is not viable so we are using a simple regex to look for word tokens.
export
const
WORD_REGEXP
=
/
[\$\_
A-Za-z
][\$\_
A-Za-z0-9
]
*/g
;
export
function
getNameValueMapFromScopeChildren
(
expressions
:
IExpression
[]):
IStringDictionary
<
string
>
{
const
nameValueMap
:
IStringDictionary
<
string
>
=
Object
.
create
(
null
);
let
valueCount
=
0
;
for
(
let
expr
of
expressions
)
{
// Put ellipses in value if its too long. Preserve last char e.g "longstr…" or {a:true, b:true, …}
let
value
=
expr
.
value
;
if
(
value
&&
value
.
length
>
MAX_INLINE_VALUE_LENGTH
)
{
value
=
value
.
substr
(
0
,
MAX_INLINE_VALUE_LENGTH
-
ELLIPSES
.
length
)
+
ELLIPSES
+
value
[
value
.
length
-
1
];
}
nameValueMap
[
expr
.
name
]
=
value
;
// Limit the size of map. Too large can have a perf impact
if
(
++
valueCount
>=
MAX_NUM_INLINE_VALUES
)
{
break
;
}
}
return
nameValueMap
;
}
export
function
getDecorators
(
nameValueMap
:
IStringDictionary
<
string
>
,
wordRangeMap
:
IStringDictionary
<
IRange
[]
>
,
linesContent
:
string
[]):
IDecorationOptions
[]
{
const
linesNames
:
IStringDictionary
<
IStringDictionary
<
boolean
>>
=
Object
.
create
(
null
);
const
names
=
Object
.
keys
(
nameValueMap
);
const
decorators
:
IDecorationOptions
[]
=
[];
// Compute unique set of names on each line
for
(
let
name
of
names
)
{
const
ranges
=
wordRangeMap
[
name
];
if
(
ranges
)
{
for
(
let
range
of
ranges
)
{
const
lineNum
=
range
.
startLineNumber
;
if
(
!
linesNames
[
lineNum
])
{
linesNames
[
lineNum
]
=
Object
.
create
(
null
);
}
linesNames
[
lineNum
][
name
]
=
true
;
}
}
}
// Compute decorators for each line
const
lineNums
=
Object
.
keys
(
linesNames
);
for
(
let
lineNum
of
lineNums
)
{
const
uniqueNames
=
Object
.
keys
(
linesNames
[
lineNum
]);
const
decorator
=
getDecoratorFromNames
(
parseInt
(
lineNum
),
uniqueNames
,
nameValueMap
,
linesContent
);
decorators
.
push
(
decorator
);
}
return
decorators
;
}
export
function
getDecoratorFromNames
(
lineNumber
:
number
,
names
:
string
[],
nameValueMap
:
IStringDictionary
<
string
>
,
linesContent
:
string
[]):
IDecorationOptions
{
const
margin
=
'
10px
'
;
const
backgroundColor
=
'
rgba(255,200,0,0.2)
'
;
const
lightForegroundColor
=
'
rgba(0,0,0,0.5)
'
;
const
darkForegroundColor
=
'
rgba(255,255,255,0.5)
'
;
const
lineLength
=
linesContent
[
lineNumber
-
1
].
length
;
// Wrap with 1em unicode space for readability
let
contentText
=
'
\
u2003
'
+
names
.
map
(
n
=>
`
${
n
}
=
${
nameValueMap
[
n
]}
`
).
join
(
'
,
'
)
+
'
\
u2003
'
;
// If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line
if
(
contentText
.
length
>
MAX_INLINE_DECORATOR_LENGTH
)
{
contentText
=
contentText
.
substr
(
0
,
MAX_INLINE_DECORATOR_LENGTH
-
ELLIPSES
.
length
)
+
ELLIPSES
;
}
const
decorator
:
IDecorationOptions
=
{
range
:
{
startLineNumber
:
lineNumber
,
endLineNumber
:
lineNumber
,
startColumn
:
lineLength
,
endColumn
:
lineLength
+
1
},
renderOptions
:
{
dark
:
{
after
:
{
contentText
,
backgroundColor
,
color
:
darkForegroundColor
,
margin
}
},
light
:
{
after
:
{
contentText
,
backgroundColor
,
color
:
lightForegroundColor
,
margin
}
}
}
};
return
decorator
;
}
export
function
getEditorWordRangeMap
(
editorModel
:
IModel
):
IStringDictionary
<
IRange
[]
>
{
const
wordRangeMap
:
IStringDictionary
<
IRange
[]
>
=
Object
.
create
(
null
);
const
linesContent
=
editorModel
.
getLinesContent
();
// For every word in every line, map its ranges for fast lookup
for
(
let
i
=
0
,
len
=
linesContent
.
length
;
i
<
len
;
++
i
)
{
const
lineContent
=
linesContent
[
i
];
// If line is too long then skip the line
if
(
lineContent
.
length
>
MAX_TOKENIZATION_LINE_LEN
)
{
continue
;
}
const
lineTokens
=
editorModel
.
getLineTokens
(
i
+
1
);
// lineNumbers are 1 based
for
(
let
j
=
0
,
len
=
lineTokens
.
getTokenCount
();
j
<
len
;
++
j
)
{
let
startOffset
=
lineTokens
.
getTokenStartOffset
(
j
);
let
endOffset
=
lineTokens
.
getTokenEndOffset
(
j
);
const
tokenStr
=
lineContent
.
substring
(
startOffset
,
endOffset
);
// Token is a word and not a comment
if
(
lineTokens
.
getStandardTokenType
(
j
)
!==
StandardTokenType
.
Comment
)
{
WORD_REGEXP
.
lastIndex
=
0
;
// We assume tokens will usually map 1:1 to words if they match
const
wordMatch
=
WORD_REGEXP
.
exec
(
tokenStr
);
if
(
wordMatch
)
{
const
word
=
wordMatch
[
0
];
startOffset
+=
wordMatch
.
index
;
endOffset
=
startOffset
+
word
.
length
;
const
range
:
IRange
=
{
startColumn
:
startOffset
+
1
,
// Line and columns are 1 based
endColumn
:
endOffset
+
1
,
startLineNumber
:
i
+
1
,
endLineNumber
:
i
+
1
};
if
(
!
wordRangeMap
[
word
])
{
wordRangeMap
[
word
]
=
[];
}
wordRangeMap
[
word
].
push
(
range
);
}
}
}
}
return
wordRangeMap
;
}
src/vs/workbench/parts/debug/test/electron-browser/debugInlineDecorators.test.ts
0 → 100644
浏览文件 @
df2d1d26
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
*
as
assert
from
'
assert
'
;
import
{
IStringDictionary
}
from
'
vs/base/common/collections
'
;
import
{
Model
as
EditorModel
}
from
'
vs/editor/common/model/model
'
;
import
{
IRange
,
IModel
}
from
'
vs/editor/common/editorCommon
'
;
import
{
StandardTokenType
}
from
'
vs/editor/common/modes
'
;
import
{
LineTokens
}
from
'
vs/editor/common/core/lineTokens
'
;
import
{
IExpression
}
from
'
vs/workbench/parts/debug/common/debug
'
;
import
*
as
inlineDecorators
from
'
vs/workbench/parts/debug/electron-browser/debugInlineDecorators
'
;
// Test data
const
testLine
=
'
function doit(everything, is, awesome, awesome, when, youre, part, of, a, team){}
'
;
const
testNameValueMap
=
{
everything
:
'
{emmet: true, batman: true, legoUniverse: true}
'
,
is
:
'
15
'
,
awesome
:
'
"aweeeeeeeeeeeeeeeeeeeeeeeeeeeeeeesome…"
'
,
when
:
'
true
'
,
youre
:
'
"Yes I mean you"
'
,
part
:
'
"𝄞 ♪ ♫"
'
};
suite
(
'
Debug - Inline Value Decorators
'
,
()
=>
{
test
(
'
getNameValueMapFromScopeChildren trims long values
'
,
()
=>
{
const
expressions
=
[
createExpression
(
'
hello
'
,
'
world
'
),
createExpression
(
'
blah
'
,
createLongString
())
];
const
nameValueMap
=
inlineDecorators
.
getNameValueMapFromScopeChildren
(
expressions
);
// Ensure blah is capped and ellipses added
assert
.
deepEqual
(
nameValueMap
,
{
hello
:
'
world
'
,
blah
:
'
"blah blah blah blah blah blah blah blah blah bla…"
'
});
});
test
(
'
getNameValueMapFromScopeChildren caps scopes to a MAX_NUM_INLINE_VALUES limit
'
,
()
=>
{
const
scopeChildren
:
IExpression
[][]
=
new
Array
(
5
);
const
expectedNameValueMap
:
IStringDictionary
<
string
>
=
Object
.
create
(
null
);
// 10 Stack Frames with a 100 scope expressions each
// JS Global Scope has 700+ expressions so this is close to a real world scenario
for
(
let
i
=
0
;
i
<
scopeChildren
.
length
;
i
++
)
{
const
expressions
=
new
Array
(
50
);
for
(
let
j
=
0
;
j
<
expressions
.
length
;
++
j
)
{
const
name
=
`name
${
i
}
.
${
j
}
`
;
const
val
=
`val
${
i
}
.
${
j
}
`
;
expressions
[
j
]
=
createExpression
(
name
,
val
);
if
((
i
*
expressions
.
length
+
j
)
<
inlineDecorators
.
MAX_NUM_INLINE_VALUES
)
{
expectedNameValueMap
[
name
]
=
val
;
}
}
scopeChildren
[
i
]
=
expressions
;
}
const
expressions
=
[].
concat
.
apply
([],
scopeChildren
);
const
nameValueMap
=
inlineDecorators
.
getNameValueMapFromScopeChildren
(
expressions
);
assert
.
deepEqual
(
nameValueMap
,
expectedNameValueMap
);
});
test
(
'
getDecoratorFromNames caps long decorator afterText
'
,
()
=>
{
const
names
=
Object
.
keys
(
testNameValueMap
);
const
lineNumber
=
1
;
const
decorator
=
inlineDecorators
.
getDecoratorFromNames
(
lineNumber
,
names
,
testNameValueMap
,
[
testLine
]);
const
expectedDecoratorText
=
'
everything = {emmet: true, batman: true, legoUniverse: true}, is = 15, awesome = "aweeeeeeeeeeeeeeeeeeeeeeeeeeeeeeesome…", when = true, youre = "Yes…
'
;
assert
.
equal
(
decorator
.
renderOptions
.
dark
.
after
.
contentText
,
decorator
.
renderOptions
.
light
.
after
.
contentText
);
assert
.
equal
(
decorator
.
renderOptions
.
dark
.
after
.
contentText
,
expectedDecoratorText
);
assert
.
deepEqual
(
decorator
.
range
,
{
startLineNumber
:
lineNumber
,
endLineNumber
:
lineNumber
,
startColumn
:
testLine
.
length
,
endColumn
:
testLine
.
length
+
1
});
});
test
(
'
getDecorators returns correct decorator afterText
'
,
()
=>
{
const
lineContent
=
'
console.log(everything, part, part);
'
;
// part shouldn't be duplicated
const
lineNumber
=
1
;
const
wordRangeMap
=
updateWordRangeMap
(
Object
.
create
(
null
),
lineNumber
,
lineContent
);
const
decorators
=
inlineDecorators
.
getDecorators
(
testNameValueMap
,
wordRangeMap
,
[
lineContent
]);
const
expectedDecoratorText
=
'
everything = {emmet: true, batman: true, legoUniverse: true}, part = "𝄞 ♪ ♫"
'
;
assert
.
equal
(
decorators
[
0
].
renderOptions
.
dark
.
after
.
contentText
,
expectedDecoratorText
);
});
test
(
'
getEditorWordRangeMap ignores comments and long lines
'
,
()
=>
{
const
expectedWords
=
'
function, doit, everything, is, awesome, when, youre, part, of, a, team
'
.
split
(
'
,
'
);
const
editorModel
=
EditorModel
.
createFromString
(
`/** Copyright comment */\n \n
${
testLine
}
\n// Test comment\n
${
createLongString
()}
\n`
);
mockEditorModelLineTokens
(
editorModel
);
const
wordRangeMap
=
inlineDecorators
.
getEditorWordRangeMap
(
editorModel
);
const
words
=
Object
.
keys
(
wordRangeMap
);
assert
.
deepEqual
(
words
,
expectedWords
);
});
});
// Test helpers
function
createExpression
(
name
:
string
,
value
:
string
):
IExpression
{
return
{
name
,
value
,
getId
:
()
=>
name
,
hasChildren
:
false
,
getChildren
:
null
};
}
function
createLongString
():
string
{
let
longStr
=
''
;
for
(
let
i
=
0
;
i
<
100
;
++
i
)
{
longStr
+=
'
blah blah blah
'
;
}
return
`"
${
longStr
}
"`
;
}
// Simple word range creator that maches wordRegex throughout string
function
updateWordRangeMap
(
wordRangeMap
:
IStringDictionary
<
IRange
[]
>
,
lineNumber
:
number
,
lineContent
:
string
):
IStringDictionary
<
IRange
[]
>
{
const
wordRegexp
=
inlineDecorators
.
WORD_REGEXP
;
wordRegexp
.
lastIndex
=
0
;
// Reset matching
while
(
true
)
{
const
wordMatch
=
wordRegexp
.
exec
(
lineContent
);
if
(
!
wordMatch
)
{
break
;
}
const
word
=
wordMatch
[
0
];
const
startOffset
=
wordMatch
.
index
;
const
endOffset
=
startOffset
+
word
.
length
;
const
range
:
IRange
=
{
startColumn
:
startOffset
+
1
,
endColumn
:
endOffset
+
1
,
startLineNumber
:
lineNumber
,
endLineNumber
:
lineNumber
};
if
(
!
wordRangeMap
[
word
])
{
wordRangeMap
[
word
]
=
[];
}
wordRangeMap
[
word
].
push
(
range
);
}
return
wordRangeMap
;
}
interface
MockToken
{
tokenType
:
StandardTokenType
;
startOffset
:
number
;
endOffset
:
number
;
}
// Simple tokenizer that separates comments from words
function
mockLineTokens
(
lineContent
:
string
):
LineTokens
{
const
tokens
:
MockToken
[]
=
[];
if
(
lineContent
.
match
(
/^
\s
*
\/(\/
|
\*)
/
))
{
tokens
.
push
({
tokenType
:
StandardTokenType
.
Comment
,
startOffset
:
0
,
endOffset
:
lineContent
.
length
});
}
// Tokenizer should ignore pure whitespace token
else
if
(
lineContent
.
match
(
/^
\s
+$/
))
{
tokens
.
push
({
tokenType
:
StandardTokenType
.
Other
,
startOffset
:
0
,
endOffset
:
lineContent
.
length
});
}
else
{
const
wordRegexp
=
inlineDecorators
.
WORD_REGEXP
;
wordRegexp
.
lastIndex
=
0
;
while
(
true
)
{
const
wordMatch
=
wordRegexp
.
exec
(
lineContent
);
if
(
!
wordMatch
)
{
break
;
}
tokens
.
push
({
tokenType
:
StandardTokenType
.
String
,
startOffset
:
wordMatch
.
index
,
endOffset
:
wordMatch
.
index
+
wordMatch
[
0
].
length
});
}
}
return
<
LineTokens
>
{
getLineContent
:
():
string
=>
lineContent
,
getTokenCount
:
():
number
=>
tokens
.
length
,
getTokenStartOffset
:
(
tokenIndex
:
number
):
number
=>
tokens
[
tokenIndex
].
startOffset
,
getTokenEndOffset
:
(
tokenIndex
:
number
):
number
=>
tokens
[
tokenIndex
].
endOffset
,
getStandardTokenType
:
(
tokenIndex
:
number
):
StandardTokenType
=>
tokens
[
tokenIndex
].
tokenType
};
};
function
mockEditorModelLineTokens
(
editorModel
:
IModel
):
void
{
const
linesContent
=
editorModel
.
getLinesContent
();
editorModel
.
getLineTokens
=
(
lineNumber
:
number
):
LineTokens
=>
mockLineTokens
(
linesContent
[
lineNumber
-
1
]);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录