Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F94089407
PhutilInteractiveEditor.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Dec 3, 19:38
Size
6 KB
Mime Type
text/x-php
Expires
Thu, Dec 5, 19:38 (2 d)
Engine
blob
Format
Raw Data
Handle
22749012
Attached To
rPHU libphutil
PhutilInteractiveEditor.php
View Options
<?php
/**
* Edit a document interactively, by launching $EDITOR (like vi or nano).
*
* $result = id(new InteractiveEditor($document))
* ->setName('shopping_list')
* ->setLineOffset(15)
* ->editInteractively();
*
* This will launch the user's $EDITOR to edit the specified '$document', and
* return their changes into '$result'.
*
* @task create Creating a New Editor
* @task edit Editing Interactively
* @task config Configuring Options
*/
final
class
PhutilInteractiveEditor
extends
Phobject
{
private
$name
=
''
;
private
$content
=
''
;
private
$offset
=
0
;
private
$preferred
;
private
$fallback
;
/* -( Creating a New Editor )---------------------------------------------- */
/**
* Constructs an interactive editor, using the text of a document.
*
* @param string Document text.
* @return $this
*
* @task create
*/
public
function
__construct
(
$content
)
{
$this
->
setContent
(
$content
);
}
/* -( Editing Interactively )----------------------------------------------- */
/**
* Launch an editor and edit the content. The edited content will be
* returned.
*
* @return string Edited content.
* @throws Exception The editor exited abnormally or something untoward
* occurred.
*
* @task edit
*/
public
function
editInteractively
()
{
$name
=
$this
->
getName
();
$content
=
$this
->
getContent
();
if
(
phutil_is_windows
())
{
$content
=
str_replace
(
"
\n
"
,
"
\r\n
"
,
$content
);
}
$tmp
=
Filesystem
::
createTemporaryDirectory
(
'edit.'
);
$path
=
$tmp
.
DIRECTORY_SEPARATOR
.
$name
;
try
{
Filesystem
::
writeFile
(
$path
,
$content
);
}
catch
(
Exception
$ex
)
{
Filesystem
::
remove
(
$tmp
);
throw
$ex
;
}
$editor
=
$this
->
getEditor
();
$offset
=
$this
->
getLineOffset
();
$err
=
$this
->
invokeEditor
(
$editor
,
$path
,
$offset
);
if
(
$err
)
{
Filesystem
::
remove
(
$tmp
);
throw
new
Exception
(
pht
(
'Editor exited with an error code (#%d).'
,
$err
));
}
try
{
$result
=
Filesystem
::
readFile
(
$path
);
Filesystem
::
remove
(
$tmp
);
}
catch
(
Exception
$ex
)
{
Filesystem
::
remove
(
$tmp
);
throw
$ex
;
}
if
(
phutil_is_windows
())
{
$result
=
str_replace
(
"
\r\n
"
,
"
\n
"
,
$result
);
}
$this
->
setContent
(
$result
);
return
$this
->
getContent
();
}
private
function
invokeEditor
(
$editor
,
$path
,
$offset
)
{
// NOTE: Popular Windows editors like Notepad++ and GitPad do not support
// line offsets, so just ignore the offset feature on Windows. We rarely
// use it anyway.
$offset_flag
=
''
;
if
(
$offset
&&
!
phutil_is_windows
())
{
$offset
=
(
int
)
$offset
;
if
(
preg_match
(
'/^mate/'
,
$editor
))
{
$offset_flag
=
csprintf
(
'-l %d'
,
$offset
);
}
else
{
$offset_flag
=
csprintf
(
'+%d'
,
$offset
);
}
}
$cmd
=
csprintf
(
'%C %C %s'
,
$editor
,
$offset_flag
,
$path
);
return
phutil_passthru
(
'%C'
,
$cmd
);
}
/* -( Configuring Options )------------------------------------------------- */
/**
* Set the line offset where the cursor should be positioned when the editor
* opens. By default, the cursor will be positioned at the start of the
* content.
*
* @param int Line number where the cursor should be positioned.
* @return $this
*
* @task config
*/
public
function
setLineOffset
(
$offset
)
{
$this
->
offset
=
(
int
)
$offset
;
return
$this
;
}
/**
* Get the current line offset. See setLineOffset().
*
* @return int Current line offset.
*
* @task config
*/
public
function
getLineOffset
()
{
return
$this
->
offset
;
}
/**
* Set the document name. Depending on the editor, this may be exposed to
* the user and can give them a sense of what they're editing.
*
* @param string Document name.
* @return $this
*
* @task config
*/
public
function
setName
(
$name
)
{
$name
=
preg_replace
(
'/[^A-Z0-9._-]+/i'
,
''
,
$name
);
$this
->
name
=
$name
;
return
$this
;
}
/**
* Get the current document name. See @{method:setName} for details.
*
* @return string Current document name.
*
* @task config
*/
public
function
getName
()
{
if
(!
strlen
(
$this
->
name
))
{
return
'untitled'
;
}
return
$this
->
name
;
}
/**
* Set the text content to be edited.
*
* @param string New content.
* @return $this
*
* @task config
*/
public
function
setContent
(
$content
)
{
$this
->
content
=
$content
;
return
$this
;
}
/**
* Retrieve the current content.
*
* @return string
*
* @task config
*/
public
function
getContent
()
{
return
$this
->
content
;
}
/**
* Set the fallback editor program to be used if the env variable $EDITOR
* is not available and there is no `editor` binary in PATH.
*
* @param string Command-line editing program (e.g. 'emacs', 'vi')
* @return $this
*
* @task config
*/
public
function
setFallbackEditor
(
$editor
)
{
$this
->
fallback
=
$editor
;
return
$this
;
}
/**
* Set the preferred editor program. If set, this will override all other
* sources of editor configuration, like $EDITOR.
*
* @param string Command-line editing program (e.g. 'emacs', 'vi')
* @return $this
*
* @task config
*/
public
function
setPreferredEditor
(
$editor
)
{
$this
->
preferred
=
$editor
;
return
$this
;
}
/**
* Get the name of the editor program to use. The value of the environmental
* variable $EDITOR will be used if available; otherwise, the `editor` binary
* if present; otherwise the best editor will be selected.
*
* @return string Command-line editing program.
*
* @task config
*/
public
function
getEditor
()
{
if
(
$this
->
preferred
)
{
return
$this
->
preferred
;
}
$editor
=
getenv
(
'EDITOR'
);
if
(
$editor
)
{
return
$editor
;
}
if
(
$this
->
fallback
)
{
return
$this
->
fallback
;
}
$candidates
=
array
(
'editor'
,
'nano'
,
'sensible-editor'
,
'vi'
);
foreach
(
$candidates
as
$cmd
)
{
if
(
Filesystem
::
binaryExists
(
$cmd
))
{
return
$cmd
;
}
}
throw
new
Exception
(
pht
(
'Unable to launch an interactive text editor. Set the %s '
.
'environment variable to an appropriate editor.'
,
'EDITOR'
));
}
}
Event Timeline
Log In to Comment