Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
frama-c
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pub
frama-c
Commits
2932ce15
Commit
2932ce15
authored
1 year ago
by
Loïc Correnson
Browse files
Options
Downloads
Patches
Plain Diff
[dome/richtext] range proxy & viewport listener
parent
87a4ac53
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
ivette/src/dome/renderer/text/richtext.tsx
+62
-3
62 additions, 3 deletions
ivette/src/dome/renderer/text/richtext.tsx
ivette/src/sandbox/text.tsx
+24
-15
24 additions, 15 deletions
ivette/src/sandbox/text.tsx
with
86 additions
and
18 deletions
ivette/src/dome/renderer/text/richtext.tsx
+
62
−
3
View file @
2932ce15
...
...
@@ -115,6 +115,11 @@ function updateContents(view: CM.EditorView, newText: string): void {
Methods of the class are no-ops when there is no associated view, and at most
one component shall be associated with a given Text buffer at the same time.
<b>Warning:</n> do not access proxy's methods during React component
rendering since they would not be synchronized with further changes from
document or editor view. Rather, those methods shall be invoked from
React and event callbacks.
All methods are bound to `this`. */
export
class
TextProxy
{
...
...
@@ -123,6 +128,7 @@ export class TextProxy {
protected
proxy
:
View
=
null
;
constructor
()
{
this
.
range
=
this
.
range
.
bind
(
this
);
this
.
clear
=
this
.
clear
.
bind
(
this
);
this
.
append
=
this
.
append
.
bind
(
this
);
this
.
toString
=
this
.
toString
.
bind
(
this
);
...
...
@@ -135,21 +141,33 @@ export class TextProxy {
// --- Public part
/** Full document range. Remark: empty documents still have 1 (empty) line. */
range
():
Selection
{
const
view
=
this
.
proxy
;
if
(
view
===
null
)
return
emptySelection
;
const
doc
=
view
.
state
.
doc
;
return
{
offset
:
0
,
length
:
doc
.
length
,
fromLine
:
1
,
toLine
:
doc
.
lines
};
}
/** Remove all text from document. */
clear
():
void
{
const
view
=
this
.
proxy
;
if
(
view
)
dispatchContents
(
view
,
CS
.
Text
.
empty
);
}
/** Full document contents. */
toString
():
string
{
const
view
=
this
.
proxy
;
return
view
?
view
.
state
.
doc
.
toString
()
:
''
;
}
/** Appends to end of document. */
append
(
data
:
string
):
void
{
const
view
=
this
.
proxy
;
if
(
view
)
appendContents
(
view
,
data
);
}
/** Appends to end of document. */
setContents
(
data
:
string
):
void
{
const
view
=
this
.
proxy
;
if
(
view
)
dispatchContents
(
view
,
data
);
...
...
@@ -181,13 +199,19 @@ function textOf(text: string): CS.Text {
export
class
TextBuffer
extends
TextProxy
{
// --- Private part (we avoid unecessary conversions from/to text)
// --- Invariant: only one of proxy, text
&
contents holds data
// --- Invariant: only one of proxy, text
or
contents holds data
private
text
=
CS
.
Text
.
empty
;
private
contents
:
string
|
undefined
=
undefined
;
private
toText
():
CS
.
Text
{
// --- requires this.proxy is null
const
contents
=
this
.
contents
;
return
contents
===
undefined
?
this
.
text
:
textOf
(
contents
);
if
(
contents
===
undefined
)
return
this
.
text
;
const
text
=
textOf
(
contents
);
this
.
text
=
text
;
this
.
contents
=
undefined
;
// --- invariant established
return
text
;
}
/** @ignore */
...
...
@@ -210,6 +234,12 @@ export class TextBuffer extends TextProxy {
// --- Public part
range
():
Selection
{
if
(
this
.
proxy
)
return
super
.
range
();
const
doc
=
this
.
toText
();
return
{
offset
:
0
,
length
:
doc
.
length
,
fromLine
:
1
,
toLine
:
doc
.
lines
};
}
clear
():
void
{
const
view
=
this
.
proxy
;
if
(
view
)
dispatchContents
(
view
,
CS
.
Text
.
empty
);
...
...
@@ -379,6 +409,30 @@ OnSelect.pack(
}
));
/* -------------------------------------------------------------------------- */
/* --- Viewport Change Listener --- */
/* -------------------------------------------------------------------------- */
const
Viewport
=
new
Field
<
SelectionCallback
|
null
>
(
null
);
Viewport
.
pack
(
CM
.
EditorView
.
updateListener
.
computeN
(
[
Viewport
.
field
],
(
state
)
=>
{
const
callback
=
state
.
field
(
Viewport
.
field
);
if
(
callback
!==
null
)
return
[
(
updates
:
CM
.
ViewUpdate
)
=>
{
if
(
updates
.
viewportChanged
)
{
const
sel
=
updates
.
view
.
viewport
;
const
doc
=
updates
.
state
.
doc
;
callback
(
selection
(
doc
,
sel
));
}
}];
return
[];
}
));
/* -------------------------------------------------------------------------- */
/* --- Decorations --- */
/* -------------------------------------------------------------------------- */
...
...
@@ -661,6 +715,7 @@ function createView(parent: Element): CM.EditorView {
ReadOnly
,
OnChange
,
OnSelect
,
Viewport
,
Decorations
,
];
const
state
=
CS
.
EditorState
.
create
({
extensions
});
...
...
@@ -676,6 +731,7 @@ export interface TextViewProps {
readOnly
?:
boolean
;
onChange
?:
Callback
;
selection
?:
Range
;
onViewport
?:
SelectionCallback
;
onSelection
?:
SelectionCallback
;
decorations
?:
Decorations
;
lineNumbers
?:
boolean
;
...
...
@@ -701,7 +757,9 @@ export function TextView(props: TextViewProps) : JSX.Element {
// ---- readOnly, onChange, onSelection, lineNumbers
const
{
readOnly
=
false
,
onChange
=
null
,
readOnly
=
false
,
onChange
=
null
,
onViewport
:
onReview
=
null
,
onSelection
:
onSelect
=
null
,
lineNumbers
:
lines
,
showCurrentLine
:
active
,
...
...
@@ -709,6 +767,7 @@ export function TextView(props: TextViewProps) : JSX.Element {
React
.
useEffect
(()
=>
ReadOnly
.
dispatch
(
view
,
readOnly
),
[
view
,
readOnly
]);
React
.
useEffect
(()
=>
OnChange
.
dispatch
(
view
,
onChange
),
[
view
,
onChange
]);
React
.
useEffect
(()
=>
OnSelect
.
dispatch
(
view
,
onSelect
),
[
view
,
onSelect
]);
React
.
useEffect
(()
=>
Viewport
.
dispatch
(
view
,
onReview
),
[
view
,
onReview
]);
React
.
useEffect
(()
=>
LineNumbers
.
dispatch
(
view
,
lines
),
[
view
,
lines
]);
React
.
useEffect
(()
=>
ActiveLine
.
dispatch
(
view
,
active
),
[
view
,
active
]);
...
...
This diff is collapsed.
Click to expand it.
ivette/src/sandbox/text.tsx
+
24
−
15
View file @
2932ce15
...
...
@@ -44,28 +44,32 @@ import { registerSandbox } from 'ivette';
/* -------------------------------------------------------------------------- */
function
UseText
():
JSX
.
Element
{
const
[
prefix
,
setPrefix
]
=
React
.
useState
(
''
);
const
[
useLines
,
flipUseLines
]
=
Dome
.
useFlipState
(
true
);
const
[
useCurrent
,
flipUseCurrent
]
=
Dome
.
useFlipState
(
true
);
const
[
readOnly
,
flipReadOnly
]
=
Dome
.
useFlipState
(
false
);
const
[
useProxy
,
flipUseProxy
]
=
Dome
.
useFlipState
(
false
);
const
[
changed
,
setChanged
]
=
React
.
useState
(
false
);
const
[
changes
,
setChanges
]
=
React
.
useState
(
0
);
const
[
length
,
setLength
]
=
React
.
useState
(
0
);
const
[
lines
,
setLines
]
=
React
.
useState
(
1
);
const
[
s
,
onSelection
]
=
React
.
useState
(
emptySelection
);
const
[
v
,
onViewport
]
=
React
.
useState
(
emptySelection
);
const
proxy
=
React
.
useMemo
(()
=>
new
TextProxy
(),
[]);
const
buffer
=
React
.
useMemo
(()
=>
new
TextBuffer
(),
[]);
const
text
=
useProxy
?
proxy
:
buffer
;
const
updatePr
efix
=
React
.
useCallback
(
const
updatePr
oxy
=
React
.
useCallback
(
()
=>
{
const
{
length
,
toLine
}
=
text
.
range
();
setChanged
(
true
);
setChanges
((
n
)
=>
1
+
n
);
setPrefix
(
text
.
toString
().
substring
(
0
,
20
).
trim
());
setLength
(
length
);
setLines
(
toLine
);
},
[
text
]);
const
push
=
React
.
useCallback
(()
=>
{
const
n
=
Math
.
random
();
text
.
append
(
`ADDED
${
n
}
\n`
);
},
[
text
]);
const
onChange
=
Dome
.
useDebounced
(
updatePr
efix
,
200
);
const
onChange
=
Dome
.
useDebounced
(
updatePr
oxy
,
200
);
const
[
decorations
,
setDecorations
]
=
React
.
useState
<
Decoration
[]
>
([]);
const
inconsistent
=
decorations
.
length
>
0
&&
changed
;
...
...
@@ -107,8 +111,8 @@ function UseText(): JSX.Element {
}]);
},
[
decorations
,
s
]);
const
isLine
=
s
.
fromLine
===
s
.
toLine
;
const
isRange
=
s
.
length
>
0
;
const
isLine
=
s
.
fromLine
===
s
.
toLine
&&
s
.
toLine
<=
lines
;
const
isRange
=
s
.
length
>
0
&&
s
.
offset
+
s
.
length
<=
length
;
return
(
<>
...
...
@@ -135,39 +139,36 @@ function UseText(): JSX.Element {
title
=
{
useProxy
?
'
Use TextProxy
'
:
'
Use TextBuffer (persistent)
'
}
onClick
=
{
flipUseProxy
}
/>
<
Code
label
=
{
`Offset
${
s
.
offset
}
-
${
s
.
offset
+
s
.
length
}
`
}
/>
<
Code
label
=
{
`Line
${
s
.
fromLine
}
-
${
s
.
toLine
}
`
}
/>
<
Filler
/>
<
Code
icon
=
{
inconsistent
?
'
WARNING
'
:
undefined
}
title
=
{
inconsistent
?
'
Iconsistent (modified text)
'
:
undefined
}
label
=
{
`Decorations
${
decorations
.
length
}
`
}
label
=
{
`Decorations
:
${
decorations
.
length
}
`
}
/>
<
IconButton
display
=
{
isLine
}
enabled
=
{
isLine
}
icon
=
"CIRC.INFO"
title
=
"Add Gutter Decoration"
onClick
=
{
addGutterDecoration
}
/>
<
IconButton
display
=
{
isLine
}
enabled
=
{
isLine
}
icon
=
"CIRC.CHECK"
title
=
"Add Line Decoration"
onClick
=
{
addLineDecoration
}
/>
<
IconButton
display
=
{
isRange
}
enabled
=
{
isRange
}
icon
=
"CIRC.PLUS"
title
=
"Add Decoration"
onClick
=
{
addDecoration
}
/>
<
IconButton
display
=
{
decorations
.
length
>
0
}
enabled
=
{
decorations
.
length
>
0
}
kind
=
{
inconsistent
?
'
negative
'
:
'
default
'
}
icon
=
"CIRC.CLOSE"
title
=
"Clear Decorations"
onClick
=
{
clearDecorations
}
/>
<
Filler
/>
<
Code
>
{
`"
${
prefix
}
" (
${
changes
}
)`
}
</
Code
>
<
Button
label
=
"Push"
onClick
=
{
push
}
/>
<
Button
label
=
"Clear"
kind
=
'negative'
onClick
=
{
clearText
}
/>
</
ToolBar
>
...
...
@@ -176,10 +177,18 @@ function UseText(): JSX.Element {
readOnly
=
{
readOnly
}
onChange
=
{
onChange
}
onSelection
=
{
onSelection
}
onViewport
=
{
onViewport
}
decorations
=
{
decorations
}
lineNumbers
=
{
useLines
}
showCurrentLine
=
{
useCurrent
}
/>
<
ToolBar
>
<
Code
label
=
{
`Offset
${
s
.
offset
}
-
${
s
.
offset
+
s
.
length
}
/
${
length
}
`
}
/>
<
Code
label
=
{
`Line
${
s
.
fromLine
}
-
${
s
.
toLine
}
/
${
lines
}
`
}
/>
<
Code
label
=
{
`View
${
v
.
fromLine
}
-
${
v
.
toLine
}
`
}
/>
<
Filler
/>
<
Code
>
{
`Changes:
${
changes
}
`
}
</
Code
>
</
ToolBar
>
</>
);
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment