mirror of
https://github.com/mawww/kakoune.git
synced 2024-08-17 00:30:26 +03:00
Compare commits
21 Commits
4e30b4da7d
...
3f20539999
Author | SHA1 | Date | |
---|---|---|---|
|
3f20539999 | ||
|
80fcfebca8 | ||
|
cbdd200e73 | ||
|
374a7a8c99 | ||
|
6674fe7587 | ||
|
4a00a6edea | ||
|
6493ddad42 | ||
|
880ad98a30 | ||
|
0bb355557e | ||
|
ba504431dc | ||
|
c4684d0d84 | ||
|
c84942c2ac | ||
|
700265b25d | ||
|
8045712595 | ||
|
966deb514e | ||
|
fe8f0f3371 | ||
|
faf83b10e2 | ||
|
9c185249a2 | ||
|
2754e27cf2 | ||
|
b1aa9946f0 | ||
|
014f8ee55e |
@ -1,6 +1,6 @@
|
||||
freebsd_task:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
image_family: freebsd-13-3
|
||||
matrix:
|
||||
- name: freebsd_clang
|
||||
env:
|
||||
@ -27,6 +27,7 @@ linux_task:
|
||||
- name: linux_gcc
|
||||
container:
|
||||
image: gcc:10
|
||||
memory: 8G
|
||||
env:
|
||||
CXX: g++
|
||||
test_script: make CXX=$CXX -j$(nproc) test
|
||||
|
@ -41,22 +41,27 @@ highlighter is replaced with the new one.
|
||||
using the `Whitespace` face, with the following *options*:
|
||||
|
||||
*-lf* <separator>:::
|
||||
a one character long separator that will replace line feeds
|
||||
a one character long separator that will replace line feeds,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-spc* <separator>:::
|
||||
a one character long separator that will replace spaces
|
||||
a one character long separator that will replace spaces,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-nbsp* <separator>:::
|
||||
a one character long separator that will replace non-breakable spaces
|
||||
a one character long separator that will replace non-breakable spaces,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-tab* <separator>:::
|
||||
a one character long separator that will replace tabulations
|
||||
a one character long separator that will replace tabulations,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-tabpad* <separator>:::
|
||||
a one character long separator that will be appended to tabulations to honor the *tabstop* option
|
||||
|
||||
*-indent* <separator>:::
|
||||
a one character long separator that will replace the first space in indentation according to the *indentwidth* option
|
||||
a one character long separator that will replace the first space in indentation
|
||||
according to the *indentwidth* option, or an empty string to ignore them.
|
||||
This will use the `WhitespaceIndent` face.
|
||||
|
||||
*-only-trailing*:::
|
||||
|
90
rc/filetype/context.kak
Normal file
90
rc/filetype/context.kak
Normal file
@ -0,0 +1,90 @@
|
||||
# https://wiki.contextgarden.net/Main_Page
|
||||
#
|
||||
# Detection
|
||||
# ‾‾‾‾‾‾‾‾‾
|
||||
|
||||
hook global BufCreate .*\.(mkiv) %{
|
||||
set-option buffer filetype context
|
||||
}
|
||||
|
||||
# Initialization
|
||||
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
||||
|
||||
hook global WinSetOption filetype=context %(
|
||||
require-module context
|
||||
|
||||
hook window InsertChar \n -group context-indent context-indent-on-new-line
|
||||
hook -once -always window WinSetOption filetype=.* %{ remove-hooks window context-indent }
|
||||
|
||||
)
|
||||
|
||||
hook -group context-highlight global WinSetOption filetype=context %{
|
||||
add-highlighter window/context ref context
|
||||
hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/context }
|
||||
}
|
||||
|
||||
provide-module context %{
|
||||
|
||||
# Highlighters
|
||||
#‾‾‾‾‾‾‾‾‾‾‾‾
|
||||
#
|
||||
add-highlighter shared/context group
|
||||
#add-highlighter shared/context default-region group
|
||||
#
|
||||
|
||||
# Commands and control sequences for typesetting
|
||||
# ‾‾‾‾‾‾‾‾‾‾‾‾
|
||||
#add-highlighter shared/context/ regex '(\[\w+?=?\w+\])' 1:cyan 2:magenta 3:yellow
|
||||
#add-highlighter shared/context/ regex '(\[?\w-*\w+\]|\[\w*-|\W)' 1:yellow 2:magenta 3:cyan
|
||||
add-highlighter shared/context/ regex '(\[?\w*-*\w+\]|\[\w*-|\W?)' 1:yellow 2:magenta 3:cyan
|
||||
#add-highlighter shared/context/ regex '(\[\w*\W?\w+\])' 1:yellow 2:magenta 3:cyan
|
||||
#add-highlighter shared/context/ regex '(\w*|\W?\[?\W)' 1:cyan 2:yellow 3:magenta
|
||||
add-highlighter shared/context/ regex '(\[file?:\w+\])' 1:value
|
||||
add-highlighter shared/context/ regex '(\{\\em.*?\})' 1:+i
|
||||
add-highlighter shared/context/ regex '(\{\\bf.*?\})' 1:+b
|
||||
add-highlighter shared/context/ regex '(\{\\it.*?\})' 1:+i
|
||||
add-highlighter shared/context/ regex '(\{\\bi.*?\})' 1:+ib
|
||||
add-highlighter shared/context/ regex '(\{\\bs.*?\})' 1:+ib
|
||||
add-highlighter shared/context/ regex '(\\overstrikes\{.*?\})' 1:+s
|
||||
|
||||
#for more information about the commands availability and/or implementation please read the ConTeXt documentation
|
||||
#
|
||||
add-highlighter shared/context/ regex '(\h*\\start*?\w-*|FLOATtext|FLOWcell|FLOWchart|FORMULAformula|JScode|JSpreamble|LUA|MP|MPclip|MPcode|MPdefinitions|MPenvironment|MPextensions|MPinclusions|MPinitializations|MPpage|MPpositiongraphic|MPpositionmethod|MPrun|PARSEDXML|TEX|TEXpage|XML|align|alignment|allmodes|appendices|asciimode|aside|attachment|attention|backgrounds|backmatter|bar|bbordermatrix|bitmapimage|blockquote|bodymatter|boxedcolumns|btxlabeltext|btxrenderingdefinitions|buffer|but|cases|catcodetable|centeraligned|chapter|characteralign|checkedfences|chemical|chemicaltext|collect|collecting|color|colorintent|coloronly|colorset|columns|columnset|columnsetspan|combination|comment|component|concept|contextcode|contextdefinitioncode|ctxfunction|ctxfunctiondefinition|currentcolor|currentlistentrywrapper|delimited|delimitedtext|displaymath|document|effect|element|embeddedxtable|endnote|enumerate|environment|exceptions|expanded|expandedcollect|extendedcatcodetable|externalfigurecollection|facingfloat|fact|figure|figuretext|fittingpage|fixed|floatcombination|font|fontclass|fontsolution|footnote|formula|formulas|framed|framedcell|framedcontent|framedrow|framedtable|framedtext|frontmatter|goto|graphictext|gridsnapping|hanging|hboxestohbox|hbregister|head|headtext|headtext|help|helptext|hiding|highlight|hyphenation|imath|indentedtext|interaction|interactionmenu|interface|intermezzotext|intertext|item|itemgroup|itemgroupcolumns|itemize|itemize|knockout|labeltext|language|layout|leftaligned|legend|linealignment|linecorrection|linefiller|linenote|linenumbering|lines|linetable|linetablebody|linetablecell|linetablehead|localfootnotes|localfootnotes|localheadsetup|locallinecorrection|localnotes|localsetups|luacode|luaparameterset|makeup|marginblock|marginrule|markedcontent|markedpages|math|mathalignment|mathcases|mathlabeltext|mathmatrix|mathmode|mathstyle|matrices|matrix|maxaligned|mdformula|midaligned|middlealigned|middlemakeup|mixedcolumns|mode|modeset|module|moduletestsection|mpformula|multicolumns|namedsection|namedsubformulas|narrow|narrower|negative|nicelyfilledbox|nointerference|notallmodes|note|notext|notmode|operatortext|opposite|outputstream|overlay|overprint|packed|pagecolumns|pagecomment|pagefigure|pagelayout|pagemakeup|par|paragraph|paragraphs|parbuilder|part|placechemical|placefigure|placefloat|placeformula|placegraphic|placeintermezzo|placelegend|placepairedbox|placetable|positioning|positionoverlay|postponing|prefixtext|processcommacommand|processcommalist|processesassignmentcommand|processesassignmentlist|product|project|protectedcolors|publication|quotation|quote|randomized|rawsetups|readingfile|regime|register|remark|reusableMPgraphic|reusableMPgraphic|rightaligned|ruby|script|sdformula|section|sectionblock|sectionblockenvironment|sectionlevel|setups|shift|sidebar|simplecolumns|specialitem|speech|splitformula|splittext|spread|standardmakeup|staticMPfigure|staticMPgraphic|strictinspectnextcharacter|structurepageregister|style|subformulas|subject|subjectlevel|subsection|subsentence|substack|subsubject|subsubsection|subsubsubject|subsubsubsection|suffixtext|symbolset|table|tabletext|tabulate|tabulatehead|tabulatetail|taglabeltext|texcode|texdefinition|text|textbackgrounds|textcolor|textcolorintent|textflow|textmakeup|textrule|textrule|thematrix|title|tokenlist|tokens|transparent|typescript|typescriptcollection|typing|uniqueMPgraphic|unittext|unpacked|usableMPgraphic|useMPgraphic|usemathstyleparameter|userdata|usingbtxspecification|vboxregister|vboxtohbox|vboxtohboxseparator|viewerlayer|vtopregister|xcell|xcolumn|xgroup|xmldisplayverbatim|xmlinlineverbatim|xmlraw|xmlsetups|xrow|xrowgroup|xtable|xtablebody|xtablefoot|xtablehead|xtablenext)' 1:meta
|
||||
|
||||
#add-highlighter shared/context/ regex '(\h*\\stop*?\w+)' 1:meta
|
||||
|
||||
add-highlighter shared/context/ regex '(\h*\\stop*?\w-*|FLOATtext|FLOWcell|FLOWchart|FORMULAformula|JScode|JSpreamble|LUA|MP|MPclip|MPcode|MPdefinitions|MPenvironment|MPextensions|MPinclusions|MPinitializations|MPpage|MPpositiongraphic|MPpositionmethod|MPrun|PARSEDXML|TEX|TEXpage|XML|align|alignment|allmodes|appendices|asciimode|aside|attachment|attention|backgrounds|backmatter|bar|bbordermatrix|bitmapimage|blockquote|bodymatter|boxedcolumns|btxlabeltext|btxrenderingdefinitions|buffer|but|cases|catcodetable|centeraligned|chapter|characteralign|checkedfences|chemical|chemicaltext|collect|collecting|color|colorintent|coloronly|colorset|columns|columnset|columnsetspan|combination|comment|component|concept|contextcode|contextdefinitioncode|ctxfunction|ctxfunctiondefinition|currentcolor|currentlistentrywrapper|delimited|delimitedtext|displaymath|document|effect|element|embeddedxtable|endnote|enumerate|environment|exceptions|expanded|expandedcollect|extendedcatcodetable|externalfigurecollection|facingfloat|fact|figure|figuretext|fittingpage|fixed|floatcombination|font|fontclass|fontsolution|footnote|formula|formulas|framed|framedcell|framedcontent|framedrow|framedtable|framedtext|frontmatter|goto|graphictext|gridsnapping|hanging|hboxestohbox|hbregister|head|headtext|headtext|help|helptext|hiding|highlight|hyphenation|imath|indentedtext|interaction|interactionmenu|interface|intermezzotext|intertext|item|itemgroup|itemgroupcolumns|itemize|itemize|knockout|labeltext|language|layout|leftaligned|legend|linealignment|linecorrection|linefiller|linenote|linenumbering|lines|linetable|linetablebody|linetablecell|linetablehead|localfootnotes|localfootnotes|localheadsetup|locallinecorrection|localnotes|localsetups|luacode|luaparameterset|makeup|marginblock|marginrule|markedcontent|markedpages|math|mathalignment|mathcases|mathlabeltext|mathmatrix|mathmode|mathstyle|matrices|matrix|maxaligned|mdformula|midaligned|middlealigned|middlemakeup|mixedcolumns|mode|modeset|module|moduletestsection|mpformula|multicolumns|namedsection|namedsubformulas|narrow|narrower|negative|nicelyfilledbox|nointerference|notallmodes|note|notext|notmode|operatortext|opposite|outputstream|overlay|overprint|packed|pagecolumns|pagecomment|pagefigure|pagelayout|pagemakeup|par|paragraph|paragraphs|parbuilder|part|placechemical|placefigure|placefloat|placeformula|placegraphic|placeintermezzo|placelegend|placepairedbox|placetable|positioning|positionoverlay|postponing|prefixtext|processcommacommand|processcommalist|processesassignmentcommand|processesassignmentlist|product|project|protectedcolors|publication|quotation|quote|randomized|rawsetups|readingfile|regime|register|remark|reusableMPgraphic|reusableMPgraphic|rightaligned|ruby|script|sdformula|section|sectionblock|sectionblockenvironment|sectionlevel|setups|shift|sidebar|simplecolumns|specialitem|speech|splitformula|splittext|spread|standardmakeup|staticMPfigure|staticMPgraphic|strictinspectnextcharacter|structurepageregister|style|subformulas|subject|subjectlevel|subsection|subsentence|substack|subsubject|subsubsection|subsubsubject|subsubsubsection|suffixtext|symbolset|table|tabletext|tabulate|tabulatehead|tabulatetail|taglabeltext|texcode|texdefinition|text|textbackgrounds|textcolor|textcolorintent|textflow|textmakeup|textrule|textrule|thematrix|title|tokenlist|tokens|transparent|typescript|typescriptcollection|typing|uniqueMPgraphic|unittext|unpacked|usableMPgraphic|useMPgraphic|usemathstyleparameter|userdata|usingbtxspecification|vboxregister|vboxtohbox|vboxtohboxseparator|viewerlayer|vtopregister|xcell|xcolumn|xgroup|xmldisplayverbatim|xmlinlineverbatim|xmlraw|xmlsetups|xrow|xrowgroup|xtable|xtablebody|xtablefoot|xtablehead|xtablenext)' 1:meta
|
||||
|
||||
add-highlighter shared/context/ regex '(\h*\\define*?\w-*|accent|activecharacter|alternativestyle|anchor|attachment|attribute|background|bar|block|bodyfont|bodyfontenvironment|bodyfontswitch|bodyfont|breakpoint|breakpoints|btx|btxdataset|btxregister|btxrendering|buffer|button|capitals|character|characterkerning|characterspacing|chemical|chemicals|chemicalsymbol|collector|color|colorgroup|color|columnbreak|columnset|columnsetarea|columnsetspan|combination|combinedlist|command|comment|complexorsimple|complexorsimpleempty|complexorsimpleempty|complexorsimple|conversion|conversionset|conversion|counter|dataset|date|delimitedtext|description|dfont|document|effect|enumeration|expandable|expansion|externalfigure|face|facingfloat|fallbackfamily|fallbackfamily|field|fieldbody|fieldbodyset|fieldcategory|fieldstack|figuresymbol|fileconstant|filefallback|filesynonym|filler|firstline|fittingpage|float|float|font|fontalternative|fontfallback|fontfamily|fontfamilypreset|fontfamily|fontfamilypreset|fontfamily|fontfeature|fontfile|fontsize|fontsolution|fontstyle|fontsynonym|font|formula|formulaalternative|formulaframed|framed|framedcontent|framedtable|framedtext|frozenfont|globalcolor|color|globalcolor|graphictypesynonym|gridsnapping|hbox|head|headalternative|help|high|highlight|hspace|hyphenationfeatures|indentedtext|indenting|initial|insertion|interaction|interactionbar|interactionmenu|interfaceconstant|interfaceelement|interfacevariable|interlinespace|intermediatecolor|itemgroup|items|label|labelclass|layer|layerpreset|layerpreset|layout|linefiller|linenote|linenumbering|lines|list|listalternative|listextra|low|lowhigh|lowmidhigh|MPinstance|makeup|marginblock|margindata|marker|marking|mathaccent|mathalignment|mathcases|mathcommand|mathdouble|mathdoubleextensible|mathematics|mathextensible|mathfence|mathfraction|mathframed|mathmatrix|mathornament|mathover|mathoverextensible|mathovertextextensible|mathradical|mathstackers|mathstyle|mathtriplet|mathunder|mathunderextensible|mathundertextextensible|mathunstacked|measure|messageconstant|mixedcolumns|mode|multicolumns|multitonecolor|color|spotcolor|namedcolor|color|namedcolor|namespace|narrower|note|orientation|ornament|ornament|outputroutine|outputroutinecommand|overlay|page|pagebreak|pagechecker|pagecolumns|pageinjection|pageinjectionalternative|pageshift|pagestate|pairedbox|palet|palet|papersize|papersize|paragraph|paragraphs|parallel|parbuilder|periodkerning|placeholder|placement|positioning|prefixset|processcolor|color|processor|profile|program|pushbutton|pushsymbol|reference|referenceformat|register|renderingwindow|resetset|ruby|scale|script|section|sectionblock|sectionlevels|selector|separatorset|shift|sidebar|sort|sorting|spotcolor|startstop|style|styleinstance|subfield|subformula|symbol|synonym|synonyms|systemattribute|systemconstant|systemvariable|TABLEsetup|tabletemplate|tabulate|text|textbackground|textflow|textnote|tokenlist|tooltip|transparency|transparency|transparency|twopasslist|type|typeface|typescriptprefix|typescriptsynonym|typesetting|typing|unit|userdata|userdataalternative|viewerlayer|vspace|vspacing|vspacingamount|xtable|color|gridsnapping|bodyfontenvironment)' 1:meta
|
||||
|
||||
add-highlighter shared/context/ regex '(\h*\\setup*?\w-*|MPinstance|TABLE|align|attachment|background|backgroundOPT|bar|blackrules|bleeding|block|bodyfont|bookmark|btx|btxdataset|btxregister|btxrendering|buffer|button|capitals|caption|characteralign|characterkerning|chemical|clipping|collector|columnset|columnsetarea|columnsetspan|combination|combinedlist|combinedlistOPT|comment|counter|dataset|delimitedtext|description|document|effect|enumeration|expansion|externalfigure|facingfloat|fieldbody|fieldcategory|fieldcontentframed|fieldlabelframed|fieldtotalframed|filler|fillinlines|fillinrules|firstline|fittingpage|fittingpageOPT|float|floatsplitting|fontsolution|formula|formulaframed|framed|framedcontent|framedtext|head|headalternative|help|high|highlight|indentedtext|indenting|initial|insertion|interaction|interactionbar|interactionbarOPT|interactionmenu|interlinespace|itemgroup|items|label|language|layer|layerOPT|layeredtext|layout|linefiller|linenote|linenumbering|lines|linetable|list|listalternative|listextra|low|lowhigh|lowmidhigh|makeup|marginblock|margindata|marginframed|marginrule|marginruleOPT|marking|mathalignment|mathcases|mathematics|mathfence|mathfraction|mathframed|mathmatrix|mathmatrixOPT|mathornament|mathradical|mathstackers|mixedcolumns|mixedcolumnsOPT|multicolumns|multicolumnsOPT|narrower|notation|note|offset|offsetbox|orientation|outputroutine|pagechecker|pagecolumns|pagecolumnsOPT|pageinjection|pageinjectionalternative|pagestate|pairedbox|papersize|paragraph|paragraphs|parallel|periodkerning|periods|placeholder|placement|positionbar|positioning|processor|profile|referenceformat|register|renderingwindow|rotate|ruby|scale|script|sectionblock|selector|shift|sidebar|sorting|startstop|stretched|style|subformula|synonyms|tables|tabulate|textbackground|textflow|textnote|thinrules|tolerance|tooltip|type|typing|unit|userdata|userdataalternative|viewerlayer|xtable|xtableOPT)' 1:meta
|
||||
|
||||
|
||||
add-highlighter shared/context/ regex '(\h*\\use*?\w-*|typescript)' 1:meta
|
||||
|
||||
#add-highlighter shared/context/ regex '(^\\start\.*|chapter|section|subsection|subsubsection|paragraph|tabulate|itemize|enumerate|formula|columns|table|quotation|textbackgrounds|backgrounds)' 1:green
|
||||
#
|
||||
#add-highlighter shared/context/ regex '(^\\stop\.*|chapter|section|subsection|subsubsection|paragraph|tabulate|itemize|enumerate|formula|columns|table|quotation|textbackgrounds|backgrounds)' 1:green
|
||||
|
||||
#detection of any % comment string including followed by a command
|
||||
add-highlighter shared/context/ regex '(\B\h*%.*?$)' 1:comment
|
||||
|
||||
#indentation
|
||||
#
|
||||
define-command -hidden context-indent-on-new-line %{
|
||||
evaluate-commands -draft -itersel %{
|
||||
try %{ execute-keys -draft <semicolon>K<a-&> }
|
||||
try %{ execute-keys -draft kxs(\[.*\])<esc>"ay}
|
||||
try %{ execute-keys -draft Z kx <a-k>(^\h*([^\n]+)\])<ret> z i<tab> <esc> <c-r><a><c-r><a><esc>}
|
||||
#The following was the tricky part,without additional registers or an if then clause regex, for the simple reason that unlike TeX derivatives such as LaTeX in which groups are defined within \{ and \} respectively and its optional arguments' counterparts are enclosed within \[ and \], in ConTeXt however, brackets are used for both optional and mandatory arguments. So the key element here is to reverse the order, for the selection to reflect it, and it doesn't matter really whether \stop precedes \start or whether the input will attempt to modify it.
|
||||
try %{ execute-keys -draft Z jxs (^\h*\\start)<ret> d <esc> i \stop <esc>}
|
||||
# No need to close define group. It inherits most arguments from setup. if setup is needed then presumably the same method applies as here save the arguments that follow as it applies to the rest
|
||||
try %{ execute-keys -draft Z jxs (^\h*\\define\.*[^\[.*\]\n]+)<ret> d<esc>}
|
||||
try %{ execute-keys -draft Z jxs (^\h*\\setup\.*[^\[.*\]\n]+)<ret> d<esc>}
|
||||
# removing trailing \[ \] after the groups/environments so is not processed by the engine
|
||||
try %{ execute-keys -draft Z jxs (\h*\[.*\])<ret> d <esc> i <esc>}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ hook global BufCreate .*[.](ex|exs) %{
|
||||
set-option buffer filetype elixir
|
||||
}
|
||||
|
||||
hook global BufCreate .*[.]html[.]l?eex %{
|
||||
hook global BufCreate .*[.]html[.][lh]?eex %{
|
||||
set-option buffer filetype eex
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@ add-highlighter shared/elixir/single_string region "'" "(?<!\\)(?:\\\\)*'" fill
|
||||
add-highlighter shared/elixir/comment region '#' '$' fill comment
|
||||
|
||||
add-highlighter shared/elixir/leex region -match-capture '~L("""|")' '(?<!\\)(?:\\\\)*("""|")' ref eex
|
||||
add-highlighter shared/elixir/heex region -match-capture '~H("""|")' '(?<!\\)(?:\\\\)*("""|")' ref eex
|
||||
|
||||
add-highlighter shared/elixir/double_string/base default-region fill string
|
||||
add-highlighter shared/elixir/double_string/interpolation region -recurse \{ \Q#{ \} fill builtin
|
||||
|
@ -16,3 +16,17 @@ hook -group perf-report-highlight global WinSetOption filetype=perf-report %{
|
||||
|
||||
map window normal <ret> ': perf-report-focus<ret>'
|
||||
}
|
||||
|
||||
provide-module perf-annotate %{
|
||||
require-module gas
|
||||
add-highlighter shared/perf-annotate group
|
||||
add-highlighter shared/perf-annotate/gas ref gas
|
||||
add-highlighter shared/perf-annotate/above_threshold regex '^\h+([1-9]|\d{2})\.\d+\b' 0:red
|
||||
add-highlighter shared/perf-annotate/below_threshold regex '^\h+0\.\d+\b' 0:green
|
||||
}
|
||||
|
||||
hook -group perf-annotate-highlight global WinSetOption filetype=perf-annotate %{
|
||||
require-module perf-annotate
|
||||
add-highlighter window/perf-annotate ref perf-annotate
|
||||
hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/perf-annotate }
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
provide-module fifo %{
|
||||
|
||||
define-command -params .. -docstring %{
|
||||
fifo [-name <buffer-name>] [-scroll] [--] <command>...: run command in a fifo buffer
|
||||
fifo [-name <name>] [-scroll] [-script <script>] [--] <args>...: run command in a fifo buffer
|
||||
if <script> is used, eval it with <args> as '$@', else pass arguments directly to the shell
|
||||
} fifo %{ evaluate-commands %sh{
|
||||
name='*fifo*'
|
||||
while true; do
|
||||
case "$1" in
|
||||
"-scroll") scroll="-scroll"; shift ;;
|
||||
"-script") script="$2"; shift 2 ;;
|
||||
"-name") name="$2"; shift 2 ;;
|
||||
"--") shift; break ;;
|
||||
*) break ;;
|
||||
@ -14,7 +16,11 @@ define-command -params .. -docstring %{
|
||||
done
|
||||
output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-fifo.XXXXXXXX)/fifo
|
||||
mkfifo ${output}
|
||||
( eval "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
if [ -n "$script" ]; then
|
||||
( eval "$script" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
else
|
||||
( "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
fi
|
||||
|
||||
printf %s\\n "
|
||||
edit! -fifo ${output} ${scroll} ${name}
|
||||
|
@ -12,8 +12,7 @@ define-command -params .. -docstring %{
|
||||
Passing no argument will perform a literal-string grep for the current selection
|
||||
} grep %{
|
||||
evaluate-commands -try-client %opt{toolsclient} %{
|
||||
fifo -name *grep* %{
|
||||
shift 2
|
||||
fifo -name *grep* -script %{
|
||||
trap - INT QUIT
|
||||
if [ $# -eq 0 ]; then
|
||||
case "$kak_opt_grepcmd" in
|
||||
@ -29,7 +28,7 @@ define-command -params .. -docstring %{
|
||||
esac
|
||||
fi
|
||||
$kak_opt_grepcmd "$@" 2>&1 | tr -d '\r'
|
||||
} 'exit;' %arg{@} # pass arguments for "$@" above, exit to avoid evaluating them
|
||||
} -- %arg{@}
|
||||
set-option buffer filetype grep
|
||||
set-option buffer jump_current_line 0
|
||||
}
|
||||
|
@ -13,11 +13,10 @@ define-command -params .. -docstring %{
|
||||
All the optional arguments are forwarded to the make utility
|
||||
} make %{
|
||||
evaluate-commands -try-client %opt{toolsclient} %{
|
||||
fifo -scroll -name *make* %{
|
||||
shift 2
|
||||
fifo -scroll -name *make* -script %{
|
||||
trap - INT QUIT
|
||||
$kak_opt_makecmd "$@"
|
||||
} 'exit;' %arg{@} # pass arguments for "$@" above, exit to avoid evaluating them
|
||||
} -- %arg{@}
|
||||
set-option buffer filetype make
|
||||
set-option buffer jump_current_line 0
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ StringView Context::main_sel_register_value(StringView reg) const
|
||||
return RegisterManager::instance()[reg].get_main(*this, index);
|
||||
}
|
||||
|
||||
void Context::set_name(String name) {
|
||||
void Context::set_name(String name) {
|
||||
String old_name = std::exchange(m_name, std::move(name));
|
||||
hooks().run_hook(Hook::ClientRenamed, format("{}:{}", old_name, m_name), *this);
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ struct HashMap
|
||||
constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; }
|
||||
|
||||
template<typename KeyType> requires IsHashCompatible<Key, std::remove_cvref_t<KeyType>>
|
||||
constexpr EffectiveValue& operator[](KeyType&& key)
|
||||
constexpr EffectiveValue& operator[](KeyType&& key)
|
||||
{
|
||||
const auto hash = hash_value(key);
|
||||
auto index = find_index(key, hash);
|
||||
|
@ -981,18 +981,14 @@ struct ShowWhitespacesHighlighter : Highlighter
|
||||
bool only_trailing = (bool) parser.get_switch("only-trailing");
|
||||
auto get_param = [&](StringView param, StringView fallback) {
|
||||
StringView value = parser.get_switch(param).value_or(fallback);
|
||||
if (value.char_length() != 1)
|
||||
throw runtime_error{format("-{} expects a single character parameter", param)};
|
||||
if (value.char_length() > 1)
|
||||
throw runtime_error{format("-{} expects a single character or empty parameter", param)};
|
||||
return value.str();
|
||||
};
|
||||
|
||||
String indent = parser.get_switch("indent").value_or("│").str();
|
||||
if (indent.char_length() > 1)
|
||||
throw runtime_error{format("-indent expects a single character or empty parameter")};
|
||||
|
||||
return std::make_unique<ShowWhitespacesHighlighter>(
|
||||
get_param("tab", "→"), get_param("tabpad", " "), get_param("spc", "·"),
|
||||
get_param("lf", "¬"), get_param("nbsp", "⍽"), indent, only_trailing);
|
||||
get_param("lf", "¬"), get_param("nbsp", "⍽"), get_param("indent", "│"), only_trailing);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1043,28 +1039,28 @@ private:
|
||||
if (it != end)
|
||||
atom_it = line.split(atom_it, it.coord());
|
||||
|
||||
if (cp == '\t')
|
||||
if (cp == '\t' and not m_tab.empty() and not m_tabpad.empty())
|
||||
{
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
const ColumnCount count = tabstop - (column % tabstop);
|
||||
atom_it->replace(m_tab + String(m_tabpad[(CharCount)0], count - m_tab.column_length()));
|
||||
}
|
||||
else if (cp == ' ') {
|
||||
if (m_indent.empty() or indentwidth == 0 or not is_indentation) {
|
||||
else if (cp == ' ' and is_indentation and indentwidth > 0 and not m_indent.empty()) {
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
if (column % indentwidth == 0 and column != 0) {
|
||||
atom_it->replace(m_indent);
|
||||
face = indentface;
|
||||
}
|
||||
else {
|
||||
atom_it->replace(m_spc);
|
||||
} else {
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
if (column % indentwidth == 0 and column != 0) {
|
||||
atom_it->replace(m_indent);
|
||||
face = indentface;
|
||||
} else {
|
||||
atom_it->replace(m_spc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cp == '\n')
|
||||
else if (cp == ' ' and not m_spc.empty()) {
|
||||
atom_it->replace(m_spc);
|
||||
}
|
||||
else if (cp == '\n' and not m_lf.empty())
|
||||
atom_it->replace(m_lf);
|
||||
else if (cp == 0xA0 or cp == 0x202F)
|
||||
else if ((cp == 0xA0 or cp == 0x202F) and not m_nbsp.empty())
|
||||
atom_it->replace(m_nbsp);
|
||||
atom_it->face = merge_faces(atom_it->face, face);
|
||||
break;
|
||||
|
@ -589,13 +589,13 @@ void pipe(Context& context, NormalParams params)
|
||||
return;
|
||||
|
||||
Buffer& buffer = context.buffer();
|
||||
SelectionList selections = context.selections();
|
||||
if (replace)
|
||||
{
|
||||
buffer.throw_if_read_only();
|
||||
ScopedEdition edition(context);
|
||||
ForwardChangesTracker changes_tracker;
|
||||
size_t timestamp = buffer.timestamp();
|
||||
Vector<Selection> new_sels;
|
||||
SelectionList selections = context.selections();
|
||||
for (auto& sel : selections)
|
||||
{
|
||||
const auto beg = changes_tracker.get_new_coord_tolerant(sel.min());
|
||||
@ -614,20 +614,27 @@ void pipe(Context& context, NormalParams params)
|
||||
|
||||
auto new_end = apply_diff(buffer, beg, in, out);
|
||||
if (new_end != beg)
|
||||
new_sels.push_back(keep_direction({beg, buffer.char_prev(new_end), std::move(sel.captures())}, sel));
|
||||
{
|
||||
auto& min = sel.min();
|
||||
auto& max = sel.max();
|
||||
min = beg;
|
||||
max = buffer.char_prev(new_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new_end != BufferCoord{})
|
||||
new_end = buffer.char_prev(new_end);
|
||||
new_sels.push_back({new_end, new_end, std::move(sel.captures())});
|
||||
sel.set(new_end);
|
||||
}
|
||||
|
||||
changes_tracker.update(buffer, timestamp);
|
||||
}
|
||||
context.selections_write_only().set(std::move(new_sels), selections.main_index());
|
||||
selections.force_timestamp(timestamp);
|
||||
context.selections_write_only() = std::move(selections);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionList& selections = context.selections();
|
||||
const auto old_main = selections.main_index();
|
||||
for (int i = 0; i < selections.size(); ++i)
|
||||
{
|
||||
|
@ -706,7 +706,7 @@ private:
|
||||
{
|
||||
auto& node = get_node(index);
|
||||
|
||||
const uint32_t start_pos = (uint32_t)m_program.instructions.size();
|
||||
const OpIndex start_pos = op_count();
|
||||
const bool ignore_case = node.ignore_case;
|
||||
|
||||
const bool save = (node.op == ParsedRegex::Alternation or node.op == ParsedRegex::Sequence) and
|
||||
@ -746,14 +746,17 @@ private:
|
||||
if (child != index+1)
|
||||
push_inst(CompiledRegex::Split);
|
||||
}
|
||||
auto split_pos = m_program.instructions.size();
|
||||
auto split_pos = op_count();
|
||||
|
||||
const auto end = node.children_end;
|
||||
for (auto child : Children<>{m_parsed_regex, index})
|
||||
{
|
||||
auto node = compile_node<direction>(child);
|
||||
if (child != index+1)
|
||||
m_program.instructions[--split_pos].param.split = CompiledRegex::Param::Split{.target = node, .prioritize_parent = true};
|
||||
{
|
||||
--split_pos;
|
||||
m_program.instructions[split_pos].param.split = {.offset = offset(node, split_pos), .prioritize_parent = true};
|
||||
}
|
||||
if (get_node(child).children_end != end)
|
||||
{
|
||||
auto jump = push_inst(CompiledRegex::Jump);
|
||||
@ -801,8 +804,8 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& offset : goto_inner_end_offsets)
|
||||
m_program.instructions[offset].param.jump_target = m_program.instructions.size();
|
||||
for (auto& index : goto_inner_end_offsets)
|
||||
m_program.instructions[index].param.jump_offset = offset(op_count(), index);
|
||||
|
||||
if (save)
|
||||
push_inst(CompiledRegex::Save, {.save_index=int16_t(node.value * 2 + (forward ? 1 : 0))});
|
||||
@ -810,19 +813,29 @@ private:
|
||||
return start_pos;
|
||||
}
|
||||
|
||||
OpIndex op_count() const
|
||||
{
|
||||
return static_cast<OpIndex>(m_program.instructions.size());
|
||||
}
|
||||
|
||||
static OpIndex offset(OpIndex to, OpIndex from)
|
||||
{
|
||||
return static_cast<OpIndex>(to - from);
|
||||
}
|
||||
|
||||
template<RegexMode direction>
|
||||
OpIndex compile_node(ParsedRegex::NodeIndex index)
|
||||
{
|
||||
auto& node = get_node(index);
|
||||
|
||||
const OpIndex start_pos = (OpIndex)m_program.instructions.size();
|
||||
const OpIndex start_pos = op_count();
|
||||
Vector<OpIndex> goto_ends;
|
||||
|
||||
auto& quantifier = node.quantifier;
|
||||
|
||||
if (quantifier.allows_none())
|
||||
{
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.target=0, .prioritize_parent=quantifier.greedy}});
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.offset=0, .prioritize_parent=quantifier.greedy}});
|
||||
goto_ends.push_back(split_pos);
|
||||
}
|
||||
|
||||
@ -832,18 +845,18 @@ private:
|
||||
inner_pos = compile_node_inner<direction>(index);
|
||||
|
||||
if (quantifier.allows_infinite_repeat())
|
||||
push_inst(CompiledRegex::Split, {.split = {.target=inner_pos, .prioritize_parent=not quantifier.greedy}});
|
||||
push_inst(CompiledRegex::Split, {.split = {.offset=offset(inner_pos, op_count()), .prioritize_parent=not quantifier.greedy}});
|
||||
// Write the node as an optional match for the min -> max counts
|
||||
else for (int i = std::max((int16_t)1, quantifier.min); // STILL UGLY !
|
||||
i < quantifier.max; ++i)
|
||||
{
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.target=0, .prioritize_parent=quantifier.greedy}});
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.offset=0, .prioritize_parent=quantifier.greedy}});
|
||||
goto_ends.push_back(split_pos);
|
||||
compile_node_inner<direction>(index);
|
||||
}
|
||||
|
||||
for (auto offset : goto_ends)
|
||||
m_program.instructions[offset].param.split.target = m_program.instructions.size();
|
||||
for (auto index : goto_ends)
|
||||
m_program.instructions[index].param.split.offset = offset(op_count(), index);
|
||||
|
||||
return start_pos;
|
||||
}
|
||||
@ -851,7 +864,7 @@ private:
|
||||
OpIndex push_inst(CompiledRegex::Op op, CompiledRegex::Param param = {})
|
||||
{
|
||||
constexpr auto max_instructions = std::numeric_limits<OpIndex>::max();
|
||||
const auto res = m_program.instructions.size();
|
||||
const auto res = op_count();
|
||||
if (res >= max_instructions)
|
||||
throw regex_error(format("regex compiled to more than {} instructions", max_instructions));
|
||||
m_program.instructions.push_back({ op, 0, param });
|
||||
@ -1031,7 +1044,7 @@ private:
|
||||
{
|
||||
auto& inst = m_program.instructions[i];
|
||||
if (is_jump(inst.op))
|
||||
m_program.instructions[inst.param.jump_target].last_step = 0xffff; // tag as jump target
|
||||
m_program.instructions[i + inst.param.jump_offset].last_step = 0xffff; // tag as jump target
|
||||
}
|
||||
|
||||
for (auto block_begin = begin; block_begin < end; )
|
||||
@ -1071,11 +1084,11 @@ private:
|
||||
String dump_regex(const CompiledRegex& program)
|
||||
{
|
||||
String res;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
for (auto& inst : program.instructions)
|
||||
{
|
||||
char buf[20];
|
||||
format_to(buf, " {:03} ", count++);
|
||||
format_to(buf, " {:03} ", index);
|
||||
res += buf;
|
||||
switch (inst.op)
|
||||
{
|
||||
@ -1095,13 +1108,13 @@ String dump_regex(const CompiledRegex& program)
|
||||
res += format("character type {}\n", to_underlying(inst.param.character_type));
|
||||
break;
|
||||
case CompiledRegex::Jump:
|
||||
res += format("jump {}\n", inst.param.jump_target);
|
||||
res += format("jump {} ({:03})\n", inst.param.jump_offset, index + inst.param.jump_offset);
|
||||
break;
|
||||
case CompiledRegex::Split:
|
||||
{
|
||||
res += format("split (prioritize {}) {}\n",
|
||||
res += format("split (prioritize {}) {} ({:03})\n",
|
||||
(inst.param.split.prioritize_parent) ? "parent" : "child",
|
||||
inst.param.split.target);
|
||||
inst.param.split.offset, index + inst.param.split.offset);
|
||||
break;
|
||||
}
|
||||
case CompiledRegex::Save:
|
||||
@ -1135,6 +1148,7 @@ String dump_regex(const CompiledRegex& program)
|
||||
case CompiledRegex::Match:
|
||||
res += "match\n";
|
||||
}
|
||||
++index;
|
||||
}
|
||||
auto dump_start_desc = [&](const CompiledRegex::StartDesc& desc, StringView name) {
|
||||
res += name + " start desc: [";
|
||||
|
@ -54,11 +54,11 @@ struct CharacterClass
|
||||
if (ignore_case)
|
||||
cp = to_lower(cp);
|
||||
|
||||
for (auto& range : ranges)
|
||||
for (auto& [min, max] : ranges)
|
||||
{
|
||||
if (cp < range.min)
|
||||
if (cp < min)
|
||||
break;
|
||||
else if (cp <= range.max)
|
||||
else if (cp <= max)
|
||||
return not negative;
|
||||
}
|
||||
|
||||
@ -106,11 +106,11 @@ struct CompiledRegex : UseMemoryDomain<MemoryDomain::Regex>
|
||||
} literal;
|
||||
int16_t character_class_index;
|
||||
CharacterType character_type;
|
||||
int16_t jump_target;
|
||||
int16_t jump_offset;
|
||||
int16_t save_index;
|
||||
struct Split
|
||||
{
|
||||
int16_t target;
|
||||
int16_t offset;
|
||||
bool prioritize_parent : 1;
|
||||
} split;
|
||||
bool line_start;
|
||||
@ -351,10 +351,10 @@ private:
|
||||
--saves.refcount;
|
||||
};
|
||||
|
||||
struct alignas(int32_t) Thread
|
||||
struct Thread
|
||||
{
|
||||
int16_t inst;
|
||||
int16_t saves;
|
||||
const CompiledRegex::Instruction* inst;
|
||||
int saves;
|
||||
};
|
||||
|
||||
using StartDesc = CompiledRegex::StartDesc;
|
||||
@ -370,8 +370,7 @@ private:
|
||||
|
||||
// Steps a thread until it consumes the current character, matches or fail
|
||||
[[gnu::always_inline]]
|
||||
void step_thread(const CompiledRegex::Instruction* instructions, const Iterator& pos, Codepoint cp,
|
||||
uint16_t current_step, Thread thread, const ExecConfig& config)
|
||||
void step_thread(const Iterator& pos, Codepoint cp, uint16_t current_step, Thread thread, const ExecConfig& config)
|
||||
{
|
||||
auto failed = [this, &thread]() {
|
||||
release_saves(thread.saves);
|
||||
@ -382,7 +381,7 @@ private:
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto& inst = instructions[thread.inst++];
|
||||
auto& inst = *thread.inst++;
|
||||
// if this instruction was already executed for this step in another thread,
|
||||
// then this thread is redundant and can be dropped
|
||||
if (inst.last_step == current_step)
|
||||
@ -416,19 +415,20 @@ private:
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::CharClass:
|
||||
if (pos == config.end)
|
||||
return failed();
|
||||
return m_program.character_classes[inst.param.character_class_index].matches(cp) ? consumed() : failed();
|
||||
if (pos != config.end and
|
||||
m_program.character_classes[inst.param.character_class_index].matches(cp))
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::CharType:
|
||||
if (pos == config.end)
|
||||
return failed();
|
||||
return is_ctype(inst.param.character_type, cp) ? consumed() : failed();
|
||||
if (pos != config.end and is_ctype(inst.param.character_type, cp))
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::Jump:
|
||||
thread.inst = inst.param.jump_target;
|
||||
thread.inst = &inst + inst.param.jump_offset;
|
||||
break;
|
||||
case CompiledRegex::Split:
|
||||
if (auto target = inst.param.split.target;
|
||||
instructions[target].last_step != current_step)
|
||||
if (auto* target = &inst + inst.param.split.offset;
|
||||
target->last_step != current_step)
|
||||
{
|
||||
if (thread.saves >= 0)
|
||||
++m_saves[thread.saves].refcount;
|
||||
@ -478,7 +478,8 @@ private:
|
||||
m_captures = -1;
|
||||
m_threads.ensure_initial_capacity();
|
||||
|
||||
const int16_t first_inst = forward ? 0 : m_program.first_backward_inst;
|
||||
ConstArrayView<CompiledRegex::Instruction> insts{m_program.instructions};
|
||||
const auto* first_inst = insts.begin() + (forward ? 0 : m_program.first_backward_inst);
|
||||
m_threads.push_current({first_inst, -1});
|
||||
|
||||
const auto* start_desc = (forward ? m_program.forward_start_desc : m_program.backward_start_desc).get();
|
||||
@ -486,7 +487,6 @@ private:
|
||||
|
||||
constexpr bool search = mode & RegexMode::Search;
|
||||
constexpr bool any_match = mode & RegexMode::AnyMatch;
|
||||
ConstArrayView<CompiledRegex::Instruction> insts{m_program.instructions};
|
||||
uint16_t current_step = -1;
|
||||
m_found_match = false;
|
||||
while (true) // Iterate on all codepoints and once at the end
|
||||
@ -506,7 +506,7 @@ private:
|
||||
Codepoint cp = codepoint(next, config);
|
||||
|
||||
while (not m_threads.current_is_empty())
|
||||
step_thread(insts.pointer(), pos, cp, current_step, m_threads.pop_current(), config);
|
||||
step_thread(pos, cp, current_step, m_threads.pop_current(), config);
|
||||
|
||||
if (pos == config.end or
|
||||
(m_threads.next_is_empty() and (not search or m_found_match)) or
|
||||
|
@ -40,7 +40,7 @@ void HistoryRegister::set(Context& context, ConstArrayView<String> values, bool
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& entry : values | reverse())
|
||||
for (auto&& entry : values | reverse())
|
||||
{
|
||||
m_content.erase(std::remove(m_content.begin(), m_content.end(), entry), m_content.end());
|
||||
m_content.insert(m_content.begin(), entry);
|
||||
|
@ -1,22 +1,27 @@
|
||||
#include "string.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "assert.hh"
|
||||
#include "unit_tests.hh"
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Avoid including all of <algorithm> just for this.
|
||||
constexpr auto max(auto lhs, auto rhs) { return lhs > rhs ? lhs : rhs;}
|
||||
constexpr auto min(auto lhs, auto rhs) { return lhs < rhs ? lhs : rhs;}
|
||||
}
|
||||
|
||||
String::Data::Data(const char* data, size_t size, size_t capacity)
|
||||
{
|
||||
if (capacity > Short::capacity)
|
||||
{
|
||||
if (capacity & 1)
|
||||
++capacity;
|
||||
|
||||
kak_assert(capacity < Long::max_capacity);
|
||||
kak_assert(capacity <= Long::max_capacity);
|
||||
u.l.ptr = Alloc{}.allocate(capacity+1);
|
||||
u.l.size = size;
|
||||
u.l.capacity = capacity;
|
||||
u.l.capacity = (capacity & Long::max_capacity);
|
||||
u.l.mode = Long::active_mask;
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(u.l.ptr, data, size);
|
||||
@ -60,26 +65,28 @@ String::Data& String::Data::operator=(Data&& other) noexcept
|
||||
template<bool copy>
|
||||
void String::Data::reserve(size_t new_capacity)
|
||||
{
|
||||
if (capacity() != 0 and new_capacity <= capacity())
|
||||
auto const current_capacity = capacity();
|
||||
if (current_capacity != 0 and new_capacity <= current_capacity)
|
||||
return;
|
||||
|
||||
if (is_long())
|
||||
new_capacity = std::max(u.l.capacity * 2, new_capacity);
|
||||
if (!is_long() and new_capacity <= Short::capacity)
|
||||
return;
|
||||
|
||||
if (new_capacity & 1)
|
||||
++new_capacity;
|
||||
kak_assert(new_capacity <= Long::max_capacity);
|
||||
new_capacity = max(new_capacity, // Do not upgrade new_capacity to be over limit.
|
||||
min(current_capacity * 2, Long::max_capacity));
|
||||
|
||||
kak_assert(new_capacity < Long::max_capacity);
|
||||
char* new_ptr = Alloc{}.allocate(new_capacity+1);
|
||||
if (copy)
|
||||
{
|
||||
memcpy(new_ptr, data(), size()+1);
|
||||
u.l.size = size();
|
||||
}
|
||||
release();
|
||||
|
||||
u.l.size = size();
|
||||
u.l.ptr = new_ptr;
|
||||
u.l.capacity = new_capacity;
|
||||
u.l.capacity = (new_capacity & Long::max_capacity);
|
||||
u.l.mode = Long::active_mask;
|
||||
}
|
||||
|
||||
template void String::Data::reserve<true>(size_t);
|
||||
@ -89,6 +96,7 @@ void String::Data::force_size(size_t new_size)
|
||||
{
|
||||
reserve<false>(new_size);
|
||||
set_size(new_size);
|
||||
data()[new_size] = 0;
|
||||
}
|
||||
|
||||
void String::Data::append(const char* str, size_t len)
|
||||
@ -131,17 +139,47 @@ void String::Data::set_size(size_t size)
|
||||
if (is_long())
|
||||
u.l.size = size;
|
||||
else
|
||||
u.s.size = (size << 1) | 1;
|
||||
u.s.remaining_size = Short::capacity - size;
|
||||
}
|
||||
|
||||
void String::Data::set_short(const char* data, size_t size)
|
||||
{
|
||||
u.s.size = (size << 1) | 1;
|
||||
kak_assert(size <= Short::capacity);
|
||||
u.s.remaining_size = Short::capacity - size;
|
||||
if (data != nullptr)
|
||||
memcpy(u.s.string, data, size);
|
||||
u.s.string[size] = 0;
|
||||
}
|
||||
|
||||
UnitTest test_data{[]{
|
||||
using Data = String::Data;
|
||||
{ // Basic data usage.
|
||||
Data data;
|
||||
kak_assert(data.size() == 0);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.capacity() == 23);
|
||||
|
||||
// Should be SSO-ed.
|
||||
data.append("test", 4);
|
||||
kak_assert(data.size() == 4);
|
||||
kak_assert(data.capacity() == 23);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.data() == StringView("test"));
|
||||
}
|
||||
{
|
||||
char large_buf[2048];
|
||||
memset(large_buf, 'x', 2048);
|
||||
Data data(large_buf, 2048);
|
||||
kak_assert(data.size() == 2048);
|
||||
kak_assert(data.capacity() >= 2048);
|
||||
|
||||
data.clear();
|
||||
kak_assert(data.size() == 0);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.capacity() == 23);
|
||||
}
|
||||
}};
|
||||
|
||||
const String String::ms_empty;
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
#ifndef string_hh_INCLUDED
|
||||
#define string_hh_INCLUDED
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include "memory.hh"
|
||||
#include "hash.hh"
|
||||
#include "units.hh"
|
||||
#include "utf8.hh"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
@ -156,17 +157,22 @@ public:
|
||||
|
||||
// String data storage using small string optimization.
|
||||
//
|
||||
// the LSB of the last byte is used to flag if we are using the small buffer
|
||||
// or an allocated one. On big endian systems that means the allocated
|
||||
// capacity must be pair, on little endian systems that means the allocated
|
||||
// capacity cannot use its most significant byte, so we effectively limit
|
||||
// capacity to 2^24 on 32bit arch, and 2^60 on 64.
|
||||
// The MSB of the last byte is used to flag if we are using the allocated buffer
|
||||
// (1) or in-situ storage, the small one (0). That means the allocated capacity
|
||||
// cannot use its most significant byte, so we effectively limit capacity to
|
||||
// 2^24 on 32bit arch, and 2^56 on 64bit.
|
||||
//
|
||||
// There is also a special NoCopy mode in which the data referred to is un-owned.
|
||||
// It is indicated by being in Long mode with capacity == 0.
|
||||
struct Data
|
||||
{
|
||||
using Alloc = Allocator<char, MemoryDomain::String>;
|
||||
|
||||
Data() { set_empty(); }
|
||||
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data), size, 0}} {}
|
||||
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data),
|
||||
size,
|
||||
/*capacity=*/0,
|
||||
/*mode=*/Long::active_mask}} {}
|
||||
|
||||
Data(const char* data, size_t size, size_t capacity);
|
||||
Data(const char* data, size_t size) : Data(data, size, size) {}
|
||||
@ -177,8 +183,8 @@ public:
|
||||
Data& operator=(const Data& other);
|
||||
Data& operator=(Data&& other) noexcept;
|
||||
|
||||
bool is_long() const { return (u.s.size & 1) == 0; }
|
||||
size_t size() const { return is_long() ? u.l.size : (u.s.size >> 1); }
|
||||
bool is_long() const { return (u.l.mode& Long::active_mask) > 0; }
|
||||
size_t size() const { return is_long() ? u.l.size : (Short::capacity - u.s.remaining_size); }
|
||||
size_t capacity() const { return is_long() ? u.l.capacity : Short::capacity; }
|
||||
|
||||
const char* data() const { return is_long() ? u.l.ptr : u.s.string; }
|
||||
@ -194,20 +200,27 @@ public:
|
||||
private:
|
||||
struct Long
|
||||
{
|
||||
static constexpr size_t max_capacity =
|
||||
(size_t)1 << 8 * (sizeof(size_t) - 1);
|
||||
static constexpr size_t capacity_bits = CHAR_BIT * (sizeof(size_t) - 1);
|
||||
static constexpr size_t max_capacity = ((size_t)1 << capacity_bits) - 1;
|
||||
|
||||
char* ptr;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
size_t capacity : capacity_bits;
|
||||
unsigned char mode;
|
||||
static constexpr unsigned char active_mask = 0b1000'0000;
|
||||
};
|
||||
static_assert(sizeof(Long) == sizeof(char*) * 3);
|
||||
|
||||
struct Short
|
||||
{
|
||||
static constexpr size_t capacity = sizeof(Long) - 2;
|
||||
char string[capacity+1];
|
||||
unsigned char size;
|
||||
static constexpr size_t capacity = sizeof(Long) - 1;
|
||||
char string[capacity];
|
||||
// When string is full remaining_size will be 0 and be the null terminator.
|
||||
// When string is empty remaining size will be 23 (0b00010111)
|
||||
// and not collide with Long::active_mask.
|
||||
unsigned char remaining_size;
|
||||
};
|
||||
static_assert(offsetof(Long, mode) == offsetof(Short, remaining_size));
|
||||
|
||||
union
|
||||
{
|
||||
@ -217,11 +230,11 @@ public:
|
||||
|
||||
void release()
|
||||
{
|
||||
if (is_long() and u.l.capacity != 0)
|
||||
if (is_long() and (u.l.capacity != 0))
|
||||
Alloc{}.deallocate(u.l.ptr, u.l.capacity+1);
|
||||
}
|
||||
|
||||
void set_empty() { u.s.size = 1; u.s.string[0] = 0; }
|
||||
void set_empty() { u.s.remaining_size = Short::capacity; u.s.string[0] = '\0'; }
|
||||
void set_short(const char* data, size_t size);
|
||||
};
|
||||
|
||||
|
@ -202,7 +202,7 @@ InplaceString<23> to_string(Grouped val)
|
||||
InplaceString<23> res;
|
||||
for (int pos = 0, len = ungrouped.m_length; pos != len; ++pos)
|
||||
{
|
||||
if (res.m_length and ((len - pos) % 3) == 0)
|
||||
if (res.m_length and ((len - pos) % 3) == 0)
|
||||
res.m_data[res.m_length++] = ',';
|
||||
res.m_data[res.m_length++] = ungrouped.m_data[pos];
|
||||
}
|
||||
|
@ -144,10 +144,10 @@ decltype(auto) to_string(const StronglyTypedNumber<RealType, ValueType>& val)
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T> requires std::is_convertible_v<T, StringView>
|
||||
template<typename T> requires std::is_convertible_v<T, StringView>
|
||||
StringView format_param(const T& val) { return val; }
|
||||
|
||||
template<typename T> requires (not std::is_convertible_v<T, StringView>)
|
||||
template<typename T> requires (not std::is_convertible_v<T, StringView>)
|
||||
decltype(auto) format_param(const T& val) { return to_string(val); }
|
||||
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
|
||||
{
|
||||
for (int line = 0; line < (int)size.line; ++line)
|
||||
{
|
||||
auto hash = hash_line(lines[line]);
|
||||
auto hash = hash_line(lines[line]);
|
||||
if (hash == hashes[line])
|
||||
continue;
|
||||
hashes[line] = hash;
|
||||
|
Loading…
Reference in New Issue
Block a user